diff options
466 files changed, 54868 insertions, 8117 deletions
diff --git a/.travis.yml b/.travis.yml index 7f7168854..7fae56f81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,35 +5,44 @@ matrix: include: - os: linux dist: trusty - go: 1.5.4 - env: - - GO15VENDOREXPERIMENT=1 - - os: linux - dist: trusty - go: 1.6.2 - - os: linux - dist: trusty + sudo: required go: 1.7.5 + script: + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse + - sudo modprobe fuse + - sudo chmod 666 /dev/fuse + - sudo chown root:$USER /etc/fuse.conf + - go run build/ci.go install + - go run build/ci.go test -coverage - # These are the latest Go versions, only run go vet and misspell on these + # These are the latest Go versions. - os: linux dist: trusty - go: 1.8 + sudo: required + go: 1.8.1 script: + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse + - sudo modprobe fuse + - sudo chmod 666 /dev/fuse + - sudo chown root:$USER /etc/fuse.conf - go run build/ci.go install - - go run build/ci.go test -coverage -vet -misspell + - go run build/ci.go test -coverage -misspell - os: osx - go: 1.8 + go: 1.8.1 + sudo: required script: + - brew update + - brew install caskroom/cask/brew-cask + - brew cask install osxfuse - go run build/ci.go install - - go run build/ci.go test -coverage -vet -misspell + - go run build/ci.go test -coverage -misspell # This builder does the Ubuntu PPA and Linux Azure uploads - os: linux dist: trusty sudo: required - go: 1.8 + go: 1.8.1 env: - ubuntu-ppa - azure-linux @@ -62,9 +71,8 @@ matrix: - GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - GOARM=7 CC=arm-linux-gnueabihf-gcc go run build/ci.go install -arch arm - GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - # ARM64 linux builds are broken in Go 1.8 (https://github.com/golang/go/issues/19137), reenable in Go 1.8.1 - # - CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64 - # - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds + - CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64 + - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds # This builder does the Linux Azure MIPS xgo uploads - os: linux @@ -72,7 +80,7 @@ matrix: sudo: required services: - docker - go: 1.8 + go: 1.8.1 env: - azure-linux-mips script: @@ -92,24 +100,6 @@ matrix: - for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done - go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - # This builder is a temporary fallback for building ARM64 while Go 1.8 is fixed - - os: linux - dist: trusty - sudo: required - go: 1.7.5 - env: - - azure-linux-arm64-fallback - addons: - apt: - packages: - - gcc-multilib - script: - - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-aarch64-linux-gnu libc6-dev-arm64-cross - - sudo ln -s /usr/include/asm-generic /usr/include/asm - - - CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64 - - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - # This builder does the Android Maven and Azure uploads - os: linux dist: precise # Needed for the android tools @@ -130,7 +120,7 @@ matrix: - azure-android - maven-android before_install: - - curl https://storage.googleapis.com/golang/go1.8.linux-amd64.tar.gz | tar -xz + - curl https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz | tar -xz - export PATH=`pwd`/go/bin:$PATH - export GOROOT=`pwd`/go - export GOPATH=$HOME/go @@ -147,7 +137,7 @@ matrix: # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads - os: osx - go: 1.8 + go: 1.8.1 env: - azure-osx - azure-ios @@ -158,7 +148,7 @@ matrix: # Build the iOS framework and upload it to CocoaPods and Azure - gem uninstall cocoapods -a - - gem install cocoapods --pre + - gem install cocoapods - mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak - sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb @@ -169,6 +159,16 @@ matrix: - go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds + # This builder does the Azure archive purges to avoid accumulating junk + - os: linux + dist: trusty + sudo: required + go: 1.8.1 + env: + - azure-purge + script: + - go run build/ci.go purge -store gethstore/builds -days 14 + install: - go get golang.org/x/tools/cmd/cover script: @@ -40,6 +40,15 @@ test: all clean: rm -fr build/_workspace/pkg/ $(GOBIN)/* +# The devtools target installs tools required for 'go generate'. +# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'. + +devtools: + env GOBIN= go get -u golang.org/x/tools/cmd/stringer + env GOBIN= go get -u github.com/jteeuwen/go-bindata/go-bindata + env GOBIN= go get -u github.com/fjl/gencodec + env GOBIN= go install ./cmd/abigen + # Cross Compilation Targets (xgo) geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios @@ -16,7 +16,7 @@ For prerequisites and detailed build instructions please read the [Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum) on the wiki. -Building geth requires both a Go and a C compiler. +Building geth requires both a Go (version 1.7 or later) and a C compiler. You can install them using your favourite package manager. Once the dependencies are installed, run diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index 4509e222d..25b61928e 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -17,13 +17,13 @@ package bind import ( + "context" "errors" "math/big" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "golang.org/x/net/context" ) var ( diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 2e6796de6..159fca136 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -17,6 +17,7 @@ package backends import ( + "context" "errors" "fmt" "math/big" @@ -26,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -33,13 +35,8 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" - "golang.org/x/net/context" ) -// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) -var chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)} - // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. var _ bind.ContractBackend = (*SimulatedBackend)(nil) @@ -60,11 +57,12 @@ type SimulatedBackend struct { // NewSimulatedBackend creates a new binding backend using a simulated blockchain // for testing purposes. -func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend { +func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend { database, _ := ethdb.NewMemDatabase() - core.WriteGenesisBlockForTesting(database, accounts...) - blockchain, _ := core.NewBlockChain(database, chainConfig, new(pow.FakePow), new(event.TypeMux), vm.Config{}) - backend := &SimulatedBackend{database: database, blockchain: blockchain} + genesis := core.Genesis{Config: params.AllProtocolChanges, Alloc: alloc} + genesis.MustCommit(database) + blockchain, _ := core.NewBlockChain(database, genesis.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) + backend := &SimulatedBackend{database: database, blockchain: blockchain, config: genesis.Config} backend.rollback() return backend } @@ -90,7 +88,7 @@ func (b *SimulatedBackend) Rollback() { } func (b *SimulatedBackend) rollback() { - blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) + blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) } @@ -250,10 +248,10 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // Execute the call. msg := callmsg{call} - evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain) + evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(evmContext, statedb, chainConfig, vm.Config{}) + vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{}) gaspool := new(core.GasPool).AddGas(math.MaxBig256) ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() return ret, gasUsed, err @@ -274,7 +272,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) } - blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { for _, tx := range b.pendingBlock.Transactions() { block.AddTx(tx) } diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 1f11827dd..b40bd65e8 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -17,6 +17,7 @@ package bind import ( + "context" "errors" "fmt" "math/big" @@ -26,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "golang.org/x/net/context" ) // SignerFn is a signer function callback when a contract requires a method to @@ -35,7 +35,8 @@ type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Tra // CallOpts is the collection of options to fine tune a contract call request. type CallOpts struct { - Pending bool // Whether to operate on the pending state or the last known one + Pending bool // Whether to operate on the pending state or the last known one + From common.Address // Optional the sender address, otherwise the first account is used Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) } @@ -108,7 +109,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, return err } var ( - msg = ethereum.CallMsg{To: &c.address, Data: input} + msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input} ctx = ensureContext(opts.Context) code []byte output []byte diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index eb46bc081..8ea3eca41 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -169,7 +169,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) - sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}) // Deploy an interaction tester contract and call a transaction on it _, _, interactor, err := DeployInteractor(auth, sim, "Deploy string") @@ -210,7 +210,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) - sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}) // Deploy a tuple tester contract and execute a structured call on it _, _, getter, err := DeployGetter(auth, sim) @@ -242,7 +242,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) - sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}) // Deploy a tuple tester contract and execute a structured call on it _, _, tupler, err := DeployTupler(auth, sim) @@ -284,7 +284,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) - sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}) // Deploy a slice tester contract and execute a n array call on it _, _, slicer, err := DeploySlicer(auth, sim) @@ -318,7 +318,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) - sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}) // Deploy a default method invoker contract and execute its default method _, _, defaulter, err := DeployDefaulter(auth, sim) @@ -351,7 +351,7 @@ var bindTests = []struct { `[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`, ` // Create a simulator and wrap a non-deployed contract - sim := backends.NewSimulatedBackend() + sim := backends.NewSimulatedBackend(nil) nonexistent, err := NewNonExistent(common.Address{}, sim) if err != nil { @@ -387,7 +387,7 @@ var bindTests = []struct { // Generate a new random account and a funded simulator key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) - sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)}) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}) // Deploy a funky gas pattern contract _, _, limiter, err := DeployFunkyGasPattern(auth, sim) @@ -408,6 +408,45 @@ var bindTests = []struct { } `, }, + // Test that constant functions can be called from an (optional) specified address + { + `CallFrom`, + ` + contract CallFrom { + function callFrom() constant returns(address) { + return msg.sender; + } + } + `, `6060604052346000575b6086806100176000396000f300606060405263ffffffff60e060020a60003504166349f8e98281146022575b6000565b34600057602c6055565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b335b905600a165627a7a72305820aef6b7685c0fa24ba6027e4870404a57df701473fe4107741805c19f5138417c0029`, + `[{"constant":true,"inputs":[],"name":"callFrom","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}]`, + ` + // Generate a new random account and a funded simulator + key, _ := crypto.GenerateKey() + auth := bind.NewKeyedTransactor(key) + sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}) + + // Deploy a sender tester contract and execute a structured call on it + _, _, callfrom, err := DeployCallFrom(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy sender contract: %v", err) + } + sim.Commit() + + if res, err := callfrom.CallFrom(nil); err != nil { + t.Errorf("Failed to call constant function: %v", err) + } else if res != (common.Address{}) { + t.Errorf("Invalid address returned, want: %x, got: %x", (common.Address{}), res) + } + + for _, addr := range []common.Address{common.Address{}, common.Address{1}, common.Address{2}} { + if res, err := callfrom.CallFrom(&bind.CallOpts{From: addr}); err != nil { + t.Fatalf("Failed to call constant function: %v", err) + } else if res != addr { + t.Fatalf("Invalid address returned, want: %x, got: %x", addr, res) + } + } + `, + }, } // Tests that packages generated by the binder can be successfully compiled and @@ -419,7 +458,7 @@ func TestBindings(t *testing.T) { t.Skip("go sdk not found for testing") } // Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845) - linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend())\n}") + linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend(nil))\n}") linkTestDeps, err := imports.Process("", []byte(linkTestCode), nil) if err != nil { t.Fatalf("failed check for goimports symlink bug: %v", err) diff --git a/accounts/abi/bind/util.go b/accounts/abi/bind/util.go index 8348f6980..d129993ca 100644 --- a/accounts/abi/bind/util.go +++ b/accounts/abi/bind/util.go @@ -17,13 +17,13 @@ package bind import ( + "context" "fmt" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "golang.org/x/net/context" ) // WaitMined waits for tx to be mined on the blockchain. diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go index f31dbfc29..d24aa721e 100644 --- a/accounts/abi/bind/util_test.go +++ b/accounts/abi/bind/util_test.go @@ -17,6 +17,7 @@ package bind_test import ( + "context" "math/big" "testing" "time" @@ -27,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "golang.org/x/net/context" ) var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -53,9 +53,8 @@ var waitDeployedTests = map[string]struct { func TestWaitDeployed(t *testing.T) { for name, test := range waitDeployedTests { - backend := backends.NewSimulatedBackend(core.GenesisAccount{ - Address: crypto.PubkeyToAddress(testKey.PublicKey), - Balance: big.NewInt(10000000000), + backend := backends.NewSimulatedBackend(core.GenesisAlloc{ + crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)}, }) // Create the transaction. diff --git a/accounts/usbwallet/ledger_hub.go b/accounts/usbwallet/ledger_hub.go index db874f4cc..fcbc24c0f 100644 --- a/accounts/usbwallet/ledger_hub.go +++ b/accounts/usbwallet/ledger_hub.go @@ -22,6 +22,7 @@ package usbwallet import ( "errors" + "runtime" "sync" "time" @@ -55,7 +56,12 @@ type LedgerHub struct { updating bool // Whether the event notification loop is running quit chan chan error - lock sync.RWMutex + + stateLock sync.RWMutex // Protects the internals of the hub from racey access + + // TODO(karalabe): remove if hotplug lands on Windows + commsPend int // Number of operations blocking enumeration + commsLock sync.Mutex // Lock protecting the pending counter and enumeration } // NewLedgerHub creates a new hardware wallet manager for Ledger devices. @@ -76,8 +82,8 @@ func (hub *LedgerHub) Wallets() []accounts.Wallet { // Make sure the list of wallets is up to date hub.refreshWallets() - hub.lock.RLock() - defer hub.lock.RUnlock() + hub.stateLock.RLock() + defer hub.stateLock.RUnlock() cpy := make([]accounts.Wallet, len(hub.wallets)) copy(cpy, hub.wallets) @@ -88,15 +94,29 @@ func (hub *LedgerHub) Wallets() []accounts.Wallet { // list of wallets based on the found devices. func (hub *LedgerHub) refreshWallets() { // Don't scan the USB like crazy it the user fetches wallets in a loop - hub.lock.RLock() + hub.stateLock.RLock() elapsed := time.Since(hub.refreshed) - hub.lock.RUnlock() + hub.stateLock.RUnlock() if elapsed < ledgerRefreshThrottling { return } // Retrieve the current list of Ledger devices var ledgers []hid.DeviceInfo + + if runtime.GOOS == "linux" { + // hidapi on Linux opens the device during enumeration to retrieve some infos, + // breaking the Ledger protocol if that is waiting for user confirmation. This + // is a bug acknowledged at Ledger, but it won't be fixed on old devices so we + // need to prevent concurrent comms ourselves. The more elegant solution would + // be to ditch enumeration in favor of hutplug events, but that don't work yet + // on Windows so if we need to hack it anyway, this is more elegant for now. + hub.commsLock.Lock() + if hub.commsPend > 0 { // A confirmation is pending, don't refresh + hub.commsLock.Unlock() + return + } + } for _, info := range hid.Enumerate(0, 0) { // Can't enumerate directly, one valid ID is the 0 wildcard for _, id := range ledgerDeviceIDs { if info.VendorID == id.Vendor && info.ProductID == id.Product { @@ -105,8 +125,12 @@ func (hub *LedgerHub) refreshWallets() { } } } + if runtime.GOOS == "linux" { + // See rationale before the enumeration why this is needed and only on Linux. + hub.commsLock.Unlock() + } // Transform the current list of wallets into the new one - hub.lock.Lock() + hub.stateLock.Lock() wallets := make([]accounts.Wallet, 0, len(ledgers)) events := []accounts.WalletEvent{} @@ -121,7 +145,7 @@ func (hub *LedgerHub) refreshWallets() { } // If there are no more wallets or the device is before the next, wrap new wallet if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 { - wallet := &ledgerWallet{url: &url, info: ledger, log: log.New("url", url)} + wallet := &ledgerWallet{hub: hub, url: &url, info: ledger, log: log.New("url", url)} events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true}) wallets = append(wallets, wallet) @@ -140,7 +164,7 @@ func (hub *LedgerHub) refreshWallets() { } hub.refreshed = time.Now() hub.wallets = wallets - hub.lock.Unlock() + hub.stateLock.Unlock() // Fire all wallet events and return for _, event := range events { @@ -152,8 +176,8 @@ func (hub *LedgerHub) refreshWallets() { // receive notifications on the addition or removal of Ledger wallets. func (hub *LedgerHub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription { // We need the mutex to reliably start/stop the update loop - hub.lock.Lock() - defer hub.lock.Unlock() + hub.stateLock.Lock() + defer hub.stateLock.Unlock() // Subscribe the caller and track the subscriber count sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink)) @@ -182,12 +206,12 @@ func (hub *LedgerHub) updater() { hub.refreshWallets() // If all our subscribers left, stop the updater - hub.lock.Lock() + hub.stateLock.Lock() if hub.updateScope.Count() == 0 { hub.updating = false - hub.lock.Unlock() + hub.stateLock.Unlock() return } - hub.lock.Unlock() + hub.stateLock.Unlock() } } diff --git a/accounts/usbwallet/ledger_wallet.go b/accounts/usbwallet/ledger_wallet.go index c3d0f0ac8..f1beebb2c 100644 --- a/accounts/usbwallet/ledger_wallet.go +++ b/accounts/usbwallet/ledger_wallet.go @@ -21,6 +21,7 @@ package usbwallet import ( + "context" "encoding/binary" "encoding/hex" "errors" @@ -38,7 +39,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/karalabe/hid" - "golang.org/x/net/context" ) // Maximum time between wallet health checks to detect USB unplugs. @@ -83,6 +83,7 @@ var errInvalidVersionReply = errors.New("invalid version reply") // ledgerWallet represents a live USB Ledger hardware wallet. type ledgerWallet struct { + hub *LedgerHub // USB hub the device originates from (TODO(karalabe): remove if hotplug lands on Windows) url *accounts.URL // Textual URL uniquely identifying this wallet info hid.DeviceInfo // Known USB device infos about the wallet @@ -576,6 +577,17 @@ func (w *ledgerWallet) SignTx(account accounts.Account, tx *types.Transaction, c <-w.commsLock defer func() { w.commsLock <- struct{}{} }() + // Ensure the device isn't screwed with while user confirmation is pending + // TODO(karalabe): remove if hotplug lands on Windows + w.hub.commsLock.Lock() + w.hub.commsPend++ + w.hub.commsLock.Unlock() + + defer func() { + w.hub.commsLock.Lock() + w.hub.commsPend-- + w.hub.commsLock.Unlock() + }() return w.ledgerSign(path, account.Address, tx, chainID) } diff --git a/appveyor.yml b/appveyor.yml index c15696705..aecf48d47 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,8 +22,8 @@ environment: install: - rmdir C:\go /s /q - - appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.windows-%GETH_ARCH%.zip - - 7z x go1.8.windows-%GETH_ARCH%.zip -y -oC:\ > NUL + - appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.1.windows-%GETH_ARCH%.zip + - 7z x go1.8.1.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - go version - gcc --version @@ -36,4 +36,4 @@ after_build: test_script: - set CGO_ENABLED=1 - - go run build\ci.go test -vet -coverage + - go run build\ci.go test -coverage diff --git a/build/_vendor/src/golang.org/x/net/LICENSE b/build/_vendor/src/golang.org/x/net/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/build/_vendor/src/golang.org/x/net/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/build/ci-notes.md b/build/ci-notes.md index 92e7c54d0..7574bfffa 100644 --- a/build/ci-notes.md +++ b/build/ci-notes.md @@ -1,18 +1,3 @@ -# Vendored Dependencies - -Dependencies are almost all vendored in at the standard Go `/vendor` path. This allows -people to build go-ethereum using the standard toolchain without any particular package -manager. It also plays nicely with `go get`, not requiring external code to be relied on. - -The one single dependent package missing from `vendor` is `golang.org/x/net/context`. As -this is a package exposed via public library APIs, it must not be vendored as dependent -code woulnd't be able to instantiate. - -To allow reproducible builds of go-ethereum nonetheless that don't need network access -during build time to fetch `golang.org/x/net/context`, a version was copied into our repo -at the very specific `/build/_vendor` path, which is added automatically by all CI build -scripts and the makefile too. - # Debian Packaging Tagged releases and develop branch commits are available as installable Debian packages @@ -20,9 +5,9 @@ for Ubuntu. Packages are built for the all Ubuntu versions which are supported b Canonical: - Trusty Tahr (14.04 LTS) -- Wily Werewolf (15.10) - Xenial Xerus (16.04 LTS) - Yakkety Yak (16.10) +- Zesty Zapus (17.04) Packages of develop branch commits have suffix -unstable and cannot be installed alongside the stable version. Switching between release streams requires user intervention. @@ -36,18 +21,18 @@ variable which Travis CI makes available to certain builds. We want to build go-ethereum with the most recent version of Go, irrespective of the Go version that is available in the main Ubuntu repository. In order to make this possible, our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on -golang-1.7, which is co-installable alongside the regular golang package. PPA dependencies +golang-1.8, which is co-installable alongside the regular golang package. PPA dependencies can be edited at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies ## Building Packages Locally (for testing) You need to run Ubuntu to do test packaging. -Add the gophers PPA and install Go 1.7 and Debian packaging tools: +Add the gophers PPA and install Go 1.8 and Debian packaging tools: $ sudo apt-add-repository ppa:gophers/ubuntu/archive $ sudo apt-get update - $ sudo apt-get install build-essential golang-1.7 devscripts debhelper + $ sudo apt-get install build-essential golang-1.8 devscripts debhelper Create the source packages: @@ -55,10 +40,10 @@ Create the source packages: Then go into the source package directory for your running distribution and build the package: - $ cd dist/ethereum-unstable-1.5.0+xenial + $ cd dist/ethereum-unstable-1.6.0+xenial $ dpkg-buildpackage Built packages are placed in the dist/ directory. $ cd .. - $ dpkg-deb -c geth-unstable_1.5.0+xenial_amd64.deb + $ dpkg-deb -c geth-unstable_1.6.0+xenial_amd64.deb diff --git a/build/ci.go b/build/ci.go index 914ce9eae..5e63c2411 100644 --- a/build/ci.go +++ b/build/ci.go @@ -24,7 +24,7 @@ Usage: go run ci.go <command> <command flags/arguments> Available commands are: install [ -arch architecture ] [ packages... ] -- builds packages and executables - test [ -coverage ] [ -vet ] [ -misspell ] [ packages... ] -- runs the tests + test [ -coverage ] [ -misspell ] [ packages... ] -- runs the tests archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artefacts importkeys -- imports signing keys from env debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package @@ -32,6 +32,7 @@ Available commands are: aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework xgo [ -alltools ] [ options ] -- cross builds according to options + purge [ -store blobstore ] [ -days threshold ] -- purges old archives from the blobstore For all commands, -n prevents execution of external programs (dry run mode). @@ -73,42 +74,47 @@ var ( executablePath("bootnode"), executablePath("evm"), executablePath("geth"), - executablePath("swarm"), + executablePath("puppeth"), executablePath("rlpdump"), + executablePath("swarm"), } // A debian package is created for all executables listed here. debExecutables = []debExecutable{ { - Name: "geth", - Description: "Ethereum CLI client.", + Name: "abigen", + Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.", }, { Name: "bootnode", Description: "Ethereum bootnode.", }, { - Name: "rlpdump", - Description: "Developer utility tool that prints RLP structures.", - }, - { Name: "evm", Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.", }, { - Name: "swarm", - Description: "Ethereum Swarm daemon and tools", + Name: "geth", + Description: "Ethereum CLI client.", }, { - Name: "abigen", - Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.", + Name: "puppeth", + Description: "Ethereum private network manager.", + }, + { + Name: "rlpdump", + Description: "Developer utility tool that prints RLP structures.", + }, + { + Name: "swarm", + Description: "Ethereum Swarm daemon and tools", }, } // Distros for which packages are created. // Note: vivid is unsupported because there is no golang-1.6 package for it. // Note: wily is unsupported because it was officially deprecated on lanchpad. - debDistros = []string{"trusty", "xenial", "yakkety"} + debDistros = []string{"trusty", "xenial", "yakkety", "zesty"} ) var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin")) @@ -146,6 +152,8 @@ func main() { doXCodeFramework(os.Args[2:]) case "xgo": doXgo(os.Args[2:]) + case "purge": + doPurge(os.Args[2:]) default: log.Fatal("unknown command ", os.Args[1]) } @@ -162,9 +170,9 @@ func doInstall(cmdline []string) { // Check Go version. People regularly open issues about compilation // failure with outdated Go. This should save them the trouble. - if runtime.Version() < "go1.4" && !strings.HasPrefix(runtime.Version(), "devel") { + if runtime.Version() < "go1.7" && !strings.HasPrefix(runtime.Version(), "devel") { log.Println("You have Go version", runtime.Version()) - log.Println("go-ethereum requires at least Go version 1.4 and cannot") + log.Println("go-ethereum requires at least Go version 1.7 and cannot") log.Println("be compiled with an earlier version. Please upgrade your Go installation.") os.Exit(1) } @@ -173,6 +181,8 @@ func doInstall(cmdline []string) { if flag.NArg() > 0 { packages = flag.Args() } + packages = build.ExpandPackagesNoVendor(packages) + if *arch == "" || *arch == runtime.GOARCH { goinstall := goTool("install", buildFlags(env)...) goinstall.Args = append(goinstall.Args, "-v") @@ -215,20 +225,16 @@ func doInstall(cmdline []string) { } func buildFlags(env build.Environment) (flags []string) { - if os.Getenv("GO_OPENCL") != "" { - flags = append(flags, "-tags", "opencl") + var ld []string + if env.Commit != "" { + ld = append(ld, "-X", "main.gitCommit="+env.Commit) } - - // Since Go 1.5, the separator char for link time assignments - // is '=' and using ' ' prints a warning. However, Go < 1.5 does - // not support using '='. - sep := " " - if runtime.Version() > "go1.5" || strings.Contains(runtime.Version(), "devel") { - sep = "=" + if runtime.GOOS == "darwin" { + ld = append(ld, "-s") } - // Set gitCommit constant via link-time assignment. - if env.Commit != "" { - flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+env.Commit) + + if len(ld) > 0 { + flags = append(flags, "-ldflags", strings.Join(ld, " ")) } return flags } @@ -249,10 +255,7 @@ func goToolArch(arch string, subcmd string, args ...string) *exec.Cmd { cmd.Args = append(cmd.Args, []string{"-ldflags", "-extldflags -Wl,--allow-multiple-definition"}...) } } - cmd.Env = []string{ - "GO15VENDOREXPERIMENT=1", - "GOPATH=" + build.GOPATH(), - } + cmd.Env = []string{"GOPATH=" + build.GOPATH()} if arch == "" || arch == runtime.GOARCH { cmd.Env = append(cmd.Env, "GOBIN="+GOBIN) } else { @@ -274,38 +277,25 @@ func goToolArch(arch string, subcmd string, args ...string) *exec.Cmd { func doTest(cmdline []string) { var ( - vet = flag.Bool("vet", false, "Whether to run go vet") misspell = flag.Bool("misspell", false, "Whether to run the spell checker") coverage = flag.Bool("coverage", false, "Whether to record code coverage") ) flag.CommandLine.Parse(cmdline) + env := build.Env() packages := []string{"./..."} if len(flag.CommandLine.Args()) > 0 { packages = flag.CommandLine.Args() } - if len(packages) == 1 && packages[0] == "./..." { - // Resolve ./... manually since go vet will fail on vendored stuff - out, err := goTool("list", "./...").CombinedOutput() - if err != nil { - log.Fatalf("package listing failed: %v\n%s", err, string(out)) - } - packages = []string{} - for _, line := range strings.Split(string(out), "\n") { - if !strings.Contains(line, "vendor") { - packages = append(packages, strings.TrimSpace(line)) - } - } - } + packages = build.ExpandPackagesNoVendor(packages) + // Run analysis tools before the tests. - if *vet { - build.MustRun(goTool("vet", packages...)) - } + build.MustRun(goTool("vet", packages...)) if *misspell { spellcheck(packages) } // Run the actual tests. - gotest := goTool("test") + gotest := goTool("test", buildFlags(env)...) // Test a single package at a time. CI builders are slow // and some tests run into timeouts under load. gotest.Args = append(gotest.Args, "-p", "1") @@ -445,6 +435,10 @@ func maybeSkipArchive(env build.Environment) { log.Printf("skipping because this is a PR build") os.Exit(0) } + if env.IsCronJob { + log.Printf("skipping because this is a cron job") + os.Exit(0) + } if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") { log.Printf("skipping because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag) os.Exit(0) @@ -970,3 +964,62 @@ func xgoTool(args []string) *exec.Cmd { } return cmd } + +// Binary distribution cleanups + +func doPurge(cmdline []string) { + var ( + store = flag.String("store", "", `Destination from where to purge archives (usually "gethstore/builds")`) + limit = flag.Int("days", 30, `Age threshold above which to delete unstalbe archives`) + ) + flag.CommandLine.Parse(cmdline) + + if env := build.Env(); !env.IsCronJob { + log.Printf("skipping because not a cron job") + os.Exit(0) + } + // Create the azure authentication and list the current archives + auth := build.AzureBlobstoreConfig{ + Account: strings.Split(*store, "/")[0], + Token: os.Getenv("AZURE_BLOBSTORE_TOKEN"), + Container: strings.SplitN(*store, "/", 2)[1], + } + blobs, err := build.AzureBlobstoreList(auth) + if err != nil { + log.Fatal(err) + } + // Iterate over the blobs, collect and sort all unstable builds + for i := 0; i < len(blobs); i++ { + if !strings.Contains(blobs[i].Name, "unstable") { + blobs = append(blobs[:i], blobs[i+1:]...) + i-- + } + } + for i := 0; i < len(blobs); i++ { + for j := i + 1; j < len(blobs); j++ { + iTime, err := time.Parse(time.RFC1123, blobs[i].Properties.LastModified) + if err != nil { + log.Fatal(err) + } + jTime, err := time.Parse(time.RFC1123, blobs[j].Properties.LastModified) + if err != nil { + log.Fatal(err) + } + if iTime.After(jTime) { + blobs[i], blobs[j] = blobs[j], blobs[i] + } + } + } + // Filter out all archives more recent that the given threshold + for i, blob := range blobs { + timestamp, _ := time.Parse(time.RFC1123, blob.Properties.LastModified) + if time.Since(timestamp) < time.Duration(*limit)*24*time.Hour { + blobs = blobs[:i] + break + } + } + // Delete all marked as such and return + if err := build.AzureBlobstoreDelete(auth, blobs); err != nil { + log.Fatal(err) + } +} diff --git a/build/deb.control b/build/deb.control index d39393d29..7394754ef 100644 --- a/build/deb.control +++ b/build/deb.control @@ -2,7 +2,7 @@ Source: {{.Name}} Section: science Priority: extra Maintainer: {{.Author}} -Build-Depends: debhelper (>= 8.0.0), golang-1.7 +Build-Depends: debhelper (>= 8.0.0), golang-1.8 Standards-Version: 3.9.5 Homepage: https://ethereum.org Vcs-Git: git://github.com/ethereum/go-ethereum.git @@ -13,7 +13,7 @@ Architecture: any Depends: ${misc:Depends}, {{.ExeList}} Description: Meta-package to install geth and other tools Meta-package to install geth and other tools - + {{range .Executables}} Package: {{$.ExeName .}} Conflicts: {{$.ExeConflicts .}} diff --git a/build/deb.rules b/build/deb.rules index 11efe15fc..b3fe5267f 100644 --- a/build/deb.rules +++ b/build/deb.rules @@ -5,7 +5,7 @@ #export DH_VERBOSE=1 override_dh_auto_build: - build/env.sh /usr/lib/go-1.7/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}} + build/env.sh /usr/lib/go-1.8/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}} override_dh_auto_test: diff --git a/build/env.sh b/build/env.sh index af560305b..3914555d1 100755 --- a/build/env.sh +++ b/build/env.sh @@ -20,8 +20,7 @@ fi # Set up the environment to use the workspace. GOPATH="$workspace" -GO15VENDOREXPERIMENT=1 -export GOPATH GO15VENDOREXPERIMENT +export GOPATH # Run the command inside the workspace. cd "$ethdir/go-ethereum" diff --git a/cmd/evm/disasm.go b/cmd/evm/disasm.go index 01a8c60ba..a78b2a8e1 100644 --- a/cmd/evm/disasm.go +++ b/cmd/evm/disasm.go @@ -20,10 +20,10 @@ import ( "errors" "fmt" "io/ioutil" + "strings" "github.com/ethereum/go-ethereum/core/asm" cli "gopkg.in/urfave/cli.v1" - "strings" ) var disasmCommand = cli.Command{ diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go new file mode 100644 index 000000000..fd34cdec1 --- /dev/null +++ b/cmd/faucet/faucet.go @@ -0,0 +1,455 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +// faucet is a Ether faucet backed by a light client. +package main + +//go:generate go-bindata -nometadata -o website.go faucet.html + +import ( + "bytes" + "context" + "encoding/json" + "flag" + "fmt" + "html/template" + "io/ioutil" + "math/big" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethstats" + "github.com/ethereum/go-ethereum/les" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/params" + "golang.org/x/net/websocket" +) + +var ( + genesisFlag = flag.String("genesis", "", "Genesis json file to seed the chain with") + apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection") + ethPortFlag = flag.Int("ethport", 30303, "Listener port for the devp2p connection") + bootFlag = flag.String("bootnodes", "", "Comma separated bootnode enode URLs to seed with") + netFlag = flag.Int("network", 0, "Network ID to use for the Ethereum protocol") + statsFlag = flag.String("ethstats", "", "Ethstats network monitoring auth string") + + netnameFlag = flag.String("faucet.name", "", "Network name to assign to the faucet") + payoutFlag = flag.Int("faucet.amount", 1, "Number of Ethers to pay out per user request") + minutesFlag = flag.Int("faucet.minutes", 1440, "Number of minutes to wait between funding rounds") + + accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with") + accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds") + + githubUser = flag.String("github.user", "", "GitHub user to authenticate with for Gist access") + githubToken = flag.String("github.token", "", "GitHub personal token to access Gists with") + + logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet") +) + +var ( + ether = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) +) + +func main() { + // Parse the flags and set up the logger to print everything requested + flag.Parse() + log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + + // Load up and render the faucet website + tmpl, err := Asset("faucet.html") + if err != nil { + log.Crit("Failed to load the faucet template", "err", err) + } + period := fmt.Sprintf("%d minute(s)", *minutesFlag) + if *minutesFlag%60 == 0 { + period = fmt.Sprintf("%d hour(s)", *minutesFlag/60) + } + website := new(bytes.Buffer) + template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{ + "Network": *netnameFlag, + "Amount": *payoutFlag, + "Period": period, + }) + // Load and parse the genesis block requested by the user + blob, err := ioutil.ReadFile(*genesisFlag) + if err != nil { + log.Crit("Failed to read genesis block contents", "genesis", *genesisFlag, "err", err) + } + genesis := new(core.Genesis) + if err = json.Unmarshal(blob, genesis); err != nil { + log.Crit("Failed to parse genesis block json", "err", err) + } + // Convert the bootnodes to internal enode representations + var enodes []*discv5.Node + for _, boot := range strings.Split(*bootFlag, ",") { + if url, err := discv5.ParseNode(boot); err == nil { + enodes = append(enodes, url) + } else { + log.Error("Failed to parse bootnode URL", "url", boot, "err", err) + } + } + // Load up the account key and decrypt its password + if blob, err = ioutil.ReadFile(*accPassFlag); err != nil { + log.Crit("Failed to read account password contents", "file", *accPassFlag, "err", err) + } + pass := string(blob) + + ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP) + if blob, err = ioutil.ReadFile(*accJSONFlag); err != nil { + log.Crit("Failed to read account key contents", "file", *accJSONFlag, "err", err) + } + acc, err := ks.Import(blob, pass, pass) + if err != nil { + log.Crit("Failed to import faucet signer account", "err", err) + } + ks.Unlock(acc, pass) + + // Assemble and start the faucet light service + faucet, err := newFaucet(genesis, *ethPortFlag, enodes, *netFlag, *statsFlag, ks, website.Bytes()) + if err != nil { + log.Crit("Failed to start faucet", "err", err) + } + defer faucet.close() + + if err := faucet.listenAndServe(*apiPortFlag); err != nil { + log.Crit("Failed to launch faucet API", "err", err) + } +} + +// request represents an accepted funding request. +type request struct { + Username string `json:"username"` // GitHub user for displaying an avatar + Account common.Address `json:"account"` // Ethereum address being funded + Time time.Time `json:"time"` // Timestamp when te request was accepted + Tx *types.Transaction `json:"tx"` // Transaction funding the account +} + +// faucet represents a crypto faucet backed by an Ethereum light client. +type faucet struct { + config *params.ChainConfig // Chain configurations for signing + stack *node.Node // Ethereum protocol stack + client *ethclient.Client // Client connection to the Ethereum chain + index []byte // Index page to serve up on the web + + keystore *keystore.KeyStore // Keystore containing the single signer + account accounts.Account // Account funding user faucet requests + nonce uint64 // Current pending nonce of the faucet + price *big.Int // Current gas price to issue funds with + + conns []*websocket.Conn // Currently live websocket connections + history map[string]time.Time // History of users and their funding requests + reqs []*request // Currently pending funding requests + update chan struct{} // Channel to signal request updates + + lock sync.RWMutex // Lock protecting the faucet's internals +} + +func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network int, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) { + // Assemble the raw devp2p protocol stack + stack, err := node.New(&node.Config{ + Name: "geth", + Version: params.Version, + DataDir: filepath.Join(os.Getenv("HOME"), ".faucet"), + P2P: p2p.Config{ + NAT: nat.Any(), + NoDiscovery: true, + DiscoveryV5: true, + ListenAddr: fmt.Sprintf(":%d", port), + DiscoveryV5Addr: fmt.Sprintf(":%d", port+1), + MaxPeers: 25, + BootstrapNodesV5: enodes, + }, + }) + if err != nil { + return nil, err + } + // Assemble the Ethereum light client protocol + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + cfg := eth.DefaultConfig + cfg.SyncMode = downloader.LightSync + cfg.NetworkId = network + cfg.Genesis = genesis + return les.New(ctx, &cfg) + }); err != nil { + return nil, err + } + // Assemble the ethstats monitoring and reporting service' + if stats != "" { + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + var serv *les.LightEthereum + ctx.Service(&serv) + return ethstats.New(stats, nil, serv) + }); err != nil { + return nil, err + } + } + // Boot up the client and ensure it connects to bootnodes + if err := stack.Start(); err != nil { + return nil, err + } + for _, boot := range enodes { + old, _ := discover.ParseNode(boot.String()) + stack.Server().AddPeer(old) + } + // Attach to the client and retrieve and interesting metadatas + api, err := stack.Attach() + if err != nil { + stack.Stop() + return nil, err + } + client := ethclient.NewClient(api) + + return &faucet{ + config: genesis.Config, + stack: stack, + client: client, + index: index, + keystore: ks, + account: ks.Accounts()[0], + history: make(map[string]time.Time), + update: make(chan struct{}, 1), + }, nil +} + +// close terminates the Ethereum connection and tears down the faucet. +func (f *faucet) close() error { + return f.stack.Stop() +} + +// listenAndServe registers the HTTP handlers for the faucet and boots it up +// for service user funding requests. +func (f *faucet) listenAndServe(port int) error { + go f.loop() + + http.HandleFunc("/", f.webHandler) + http.Handle("/api", websocket.Handler(f.apiHandler)) + + return http.ListenAndServe(fmt.Sprintf(":%d", port), nil) +} + +// webHandler handles all non-api requests, simply flattening and returning the +// faucet website. +func (f *faucet) webHandler(w http.ResponseWriter, r *http.Request) { + w.Write(f.index) +} + +// apiHandler handles requests for Ether grants and transaction statuses. +func (f *faucet) apiHandler(conn *websocket.Conn) { + // Start tracking the connection and drop at the end + f.lock.Lock() + f.conns = append(f.conns, conn) + f.lock.Unlock() + + defer func() { + f.lock.Lock() + for i, c := range f.conns { + if c == conn { + f.conns = append(f.conns[:i], f.conns[i+1:]...) + break + } + } + f.lock.Unlock() + }() + // Send a few initial stats to the client + balance, _ := f.client.BalanceAt(context.Background(), f.account.Address, nil) + nonce, _ := f.client.NonceAt(context.Background(), f.account.Address, nil) + + websocket.JSON.Send(conn, map[string]interface{}{ + "funds": balance.Div(balance, ether), + "funded": nonce, + "peers": f.stack.Server().PeerCount(), + "requests": f.reqs, + }) + header, _ := f.client.HeaderByNumber(context.Background(), nil) + websocket.JSON.Send(conn, header) + + // Keep reading requests from the websocket until the connection breaks + for { + // Fetch the next funding request and validate against github + var msg struct { + URL string `json:"url"` + } + if err := websocket.JSON.Receive(conn, &msg); err != nil { + return + } + if !strings.HasPrefix(msg.URL, "https://gist.github.com/") { + websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"}) + continue + } + log.Info("Faucet funds requested", "gist", msg.URL) + + // Retrieve the gist from the GitHub Gist APIs + parts := strings.Split(msg.URL, "/") + req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil) + if *githubUser != "" { + req.SetBasicAuth(*githubUser, *githubToken) + } + res, err := http.DefaultClient.Do(req) + if err != nil { + websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) + continue + } + var gist struct { + Owner struct { + Login string `json:"login"` + } `json:"owner"` + Files map[string]struct { + Content string `json:"content"` + } `json:"files"` + } + err = json.NewDecoder(res.Body).Decode(&gist) + res.Body.Close() + if err != nil { + websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) + continue + } + if gist.Owner.Login == "" { + websocket.JSON.Send(conn, map[string]string{"error": "Nice try ;)"}) + continue + } + // Iterate over all the files and look for Ethereum addresses + var address common.Address + for _, file := range gist.Files { + if len(file.Content) == 2+common.AddressLength*2 { + address = common.HexToAddress(file.Content) + } + } + if address == (common.Address{}) { + websocket.JSON.Send(conn, map[string]string{"error": "No Ethereum address found to fund"}) + continue + } + // Ensure the user didn't request funds too recently + f.lock.Lock() + var ( + fund bool + elapsed time.Duration + ) + if elapsed = time.Since(f.history[gist.Owner.Login]); elapsed > time.Duration(*minutesFlag)*time.Minute { + // User wasn't funded recently, create the funding transaction + tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether), big.NewInt(21000), f.price, nil) + signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainId) + if err != nil { + websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) + f.lock.Unlock() + continue + } + // Submit the transaction and mark as funded if successful + if err := f.client.SendTransaction(context.Background(), signed); err != nil { + websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) + f.lock.Unlock() + continue + } + f.reqs = append(f.reqs, &request{ + Username: gist.Owner.Login, + Account: address, + Time: time.Now(), + Tx: signed, + }) + f.history[gist.Owner.Login] = time.Now() + fund = true + } + f.lock.Unlock() + + // Send an error if too frequent funding, othewise a success + if !fund { + websocket.JSON.Send(conn, map[string]string{"error": fmt.Sprintf("User already funded %s ago", common.PrettyDuration(elapsed))}) + continue + } + websocket.JSON.Send(conn, map[string]string{"success": fmt.Sprintf("Funding request accepted for %s into %s", gist.Owner.Login, address.Hex())}) + select { + case f.update <- struct{}{}: + default: + } + } +} + +// loop keeps waiting for interesting events and pushes them out to connected +// websockets. +func (f *faucet) loop() { + // Wait for chain events and push them to clients + heads := make(chan *types.Header, 16) + sub, err := f.client.SubscribeNewHead(context.Background(), heads) + if err != nil { + log.Crit("Failed to subscribe to head events", "err", err) + } + defer sub.Unsubscribe() + + for { + select { + case head := <-heads: + // New chain head arrived, query the current stats and stream to clients + balance, _ := f.client.BalanceAt(context.Background(), f.account.Address, nil) + balance = new(big.Int).Div(balance, ether) + + price, _ := f.client.SuggestGasPrice(context.Background()) + nonce, _ := f.client.NonceAt(context.Background(), f.account.Address, nil) + + f.lock.Lock() + f.price, f.nonce = price, nonce + for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce { + f.reqs = f.reqs[1:] + } + f.lock.Unlock() + + f.lock.RLock() + for _, conn := range f.conns { + if err := websocket.JSON.Send(conn, map[string]interface{}{ + "funds": balance, + "funded": f.nonce, + "peers": f.stack.Server().PeerCount(), + "requests": f.reqs, + }); err != nil { + log.Warn("Failed to send stats to client", "err", err) + conn.Close() + continue + } + if err := websocket.JSON.Send(conn, head); err != nil { + log.Warn("Failed to send header to client", "err", err) + conn.Close() + } + } + f.lock.RUnlock() + + case <-f.update: + // Pending requests updated, stream to clients + f.lock.RLock() + for _, conn := range f.conns { + if err := websocket.JSON.Send(conn, map[string]interface{}{"requests": f.reqs}); err != nil { + log.Warn("Failed to send requests to client", "err", err) + conn.Close() + } + } + f.lock.RUnlock() + } + } +} diff --git a/cmd/faucet/faucet.html b/cmd/faucet/faucet.html new file mode 100644 index 000000000..570145ea2 --- /dev/null +++ b/cmd/faucet/faucet.html @@ -0,0 +1,143 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <title>{{.Network}}: GitHub Faucet</title> + + <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" /> + <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" /> + + <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-noty/2.4.1/packaged/jquery.noty.packaged.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.0/moment.min.js"></script> + + <style> + .vertical-center { + min-height: 100%; + min-height: 100vh; + display: flex; + align-items: center; + } + .progress { + position: relative; + } + .progress span { + position: absolute; + display: block; + width: 100%; + color: white; + } + pre { + padding: 6px; + margin: 0; + } + </style> + </head> + + <body> + <div class="vertical-center"> + <div class="container"> + <div class="row" style="margin-bottom: 16px;"> + <div class="col-lg-12"> + <h1 style="text-align: center;"><i class="fa fa-bath" aria-hidden="true"></i> {{.Network}} GitHub Authenticated Faucet <i class="fa fa-github-alt" aria-hidden="true"></i></h1> + </div> + </div> + <div class="row"> + <div class="col-lg-8 col-lg-offset-2"> + <div class="input-group"> + <input id="gist" type="text" class="form-control" placeholder="GitHub Gist URL containing your Ethereum address..."> + <span class="input-group-btn"> + <button class="btn btn-default" type="button" onclick="submit()">Give me Ether!</button> + </span> + </div> + </div> + </div> + <div class="row" style="margin-top: 32px;"> + <div class="col-lg-6 col-lg-offset-3"> + <div class="panel panel-small panel-default"> + <div class="panel-body" style="padding: 0; overflow: auto; max-height: 300px;"> + <table id="requests" class="table table-condensed" style="margin: 0;"></table> + </div> + <div class="panel-footer"> + <table style="width: 100%"><tr> + <td style="text-align: center;"><i class="fa fa-rss" aria-hidden="true"></i> <span id="peers"></span> peers</td> + <td style="text-align: center;"><i class="fa fa-database" aria-hidden="true"></i> <span id="block"></span> blocks</td> + <td style="text-align: center;"><i class="fa fa-heartbeat" aria-hidden="true"></i> <span id="funds"></span> Ethers</td> + <td style="text-align: center;"><i class="fa fa-university" aria-hidden="true"></i> <span id="funded"></span> funded</td> + </tr></table> + </div> + </div> + </div> + </div> + <div class="row" style="margin-top: 32px;"> + <div class="col-lg-12"> + <h3>How does this work?</h3> + <p>This Ether faucet is running on the {{.Network}} network. To prevent malicious actors from exhausting all available funds or accumulating enough Ether to mount long running spam attacks, requests are tied to GitHub accounts. Anyone having a GitHub account may request funds within the permitted limit of <strong>{{.Amount}} Ether(s) / {{.Period}}</strong>.</p> + <p>To request funds, simply create a <a href="https://gist.github.com/" target="_about:blank">GitHub Gist</a> with your Ethereum address pasted into the contents (the file name doesn't matter), copy paste the gists URL into the above input box and fire away! You can track the current pending requests below the input field to see how much you have to wait until your turn comes.</p> + </div> + </div> + </div> + </div> + + <script> + // Global variables to hold the current status of the faucet + var attempt = 0; + var server; + + // Define the function that submits a gist url to the server + var submit = function() { + server.send(JSON.stringify({url: $("#gist")[0].value})); + }; + // Define a method to reconnect upon server loss + var reconnect = function() { + if (attempt % 2 == 0) { + server = new WebSocket("wss://" + location.host + "/api"); + } else { + server = new WebSocket("ws://" + location.host + "/api"); + } + attempt++; + + server.onmessage = function(event) { + var msg = JSON.parse(event.data); + if (msg === null) { + return; + } + + if (msg.funds !== undefined) { + $("#funds").text(msg.funds); + } + if (msg.funded !== undefined) { + $("#funded").text(msg.funded); + } + if (msg.peers !== undefined) { + $("#peers").text(msg.peers); + } + if (msg.number !== undefined) { + $("#block").text(parseInt(msg.number, 16)); + } + if (msg.error !== undefined) { + noty({layout: 'topCenter', text: msg.error, type: 'error'}); + } + if (msg.success !== undefined) { + noty({layout: 'topCenter', text: msg.success, type: 'success'}); + } + if (msg.requests !== undefined && msg.requests !== null) { + var content = ""; + for (var i=0; i<msg.requests.length; i++) { + content += "<tr><td><div style=\"background: url('https://github.com/" + msg.requests[i].username + ".png?size=64'); background-size: cover; width:32px; height: 32px; border-radius: 4px;\"></div></td><td><pre>" + msg.requests[i].account + "</pre></td><td style=\"width: 100%; text-align: center; vertical-align: middle;\">" + moment.duration(moment(msg.requests[i].time).unix()-moment().unix(), 'seconds').humanize(true) + "</td></tr>"; + } + $("#requests").html("<tbody>" + content + "</tbody>"); + } + } + server.onclose = function() { setTimeout(reconnect, 3000); }; + server.onerror = function() { setTimeout(reconnect, 3000); }; + } + // Establish a websocket connection to the API server + reconnect(); + </script> + </body> +</html> diff --git a/cmd/faucet/website.go b/cmd/faucet/website.go new file mode 100644 index 000000000..32650fec4 --- /dev/null +++ b/cmd/faucet/website.go @@ -0,0 +1,235 @@ +// Code generated by go-bindata. +// sources: +// faucet.html +// DO NOT EDIT! + +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _faucetHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x58\xe1\x72\xdb\x36\x12\xfe\x2d\x3f\xc5\x86\x77\xad\xa5\xb1\x49\xca\x71\x26\xed\xc8\xa4\x3a\x99\x36\x97\xf6\xe6\xa6\xed\x5c\xd3\xb9\xeb\xb4\x9d\x1b\x90\x5c\x8a\xb0\x41\x80\x05\x16\x92\xd5\x8c\xde\xfd\x06\x00\x45\x51\x8a\x9d\xa4\x97\xde\x1f\x5b\x00\x16\xdf\x7e\xd8\x5d\xec\x2e\x98\x3d\xf9\xea\xbb\x2f\x5f\xff\xf4\xfd\x4b\x68\xa8\x15\xcb\xb3\xcc\xfd\x03\xc1\xe4\x2a\x8f\x50\x46\xcb\xb3\x49\xd6\x20\xab\x96\x67\x93\x49\xd6\x22\x31\x28\x1b\xa6\x0d\x52\x1e\x59\xaa\xe3\xcf\xa3\xc3\x42\x43\xd4\xc5\xf8\x9b\xe5\xeb\x3c\xfa\x77\xfc\xe3\x8b\xf8\x4b\xd5\x76\x8c\x78\x21\x30\x82\x52\x49\x42\x49\x79\xf4\xcd\xcb\x1c\xab\x15\x8e\xf6\x49\xd6\x62\x1e\xad\x39\x6e\x3a\xa5\x69\x24\xba\xe1\x15\x35\x79\x85\x6b\x5e\x62\xec\x07\x97\xc0\x25\x27\xce\x44\x6c\x4a\x26\x30\xbf\x8a\x96\x67\x0e\x87\x38\x09\x5c\xbe\x79\x93\x7c\x8b\xb4\x51\xfa\x6e\xb7\x5b\xc0\x2b\x4e\x5f\xdb\x02\xfe\xc6\x6c\x89\x94\xa5\x41\xc4\x4b\x0b\x2e\xef\xa0\xd1\x58\xe7\x91\xe3\x6c\x16\x69\x5a\x56\xf2\xd6\x24\xa5\x50\xb6\xaa\x05\xd3\x98\x94\xaa\x4d\xd9\x2d\xbb\x4f\x05\x2f\x4c\x4a\x1b\x4e\x84\x3a\x2e\x94\x22\x43\x9a\x75\xe9\x75\x72\x9d\x7c\x96\x96\xc6\xa4\xc3\x5c\xd2\x72\x99\x94\xc6\x44\xa0\x51\xe4\x91\xa1\xad\x40\xd3\x20\x52\x04\xe9\xf2\x7f\xd3\x5b\x2b\x49\x31\xdb\xa0\x51\x2d\xa6\xcf\x92\xcf\x92\xb9\x57\x39\x9e\x7e\xb7\x56\xa7\xd6\x94\x9a\x77\x04\x46\x97\x1f\xac\xf7\xf6\x37\x8b\x7a\x9b\x5e\x27\x57\xc9\x55\x3f\xf0\x7a\x6e\x4d\xb4\xcc\xd2\x00\xb8\xfc\x28\xec\x58\x2a\xda\xa6\x4f\x93\x67\xc9\x55\xda\xb1\xf2\x8e\xad\xb0\xda\x6b\x72\x4b\xc9\x7e\xf2\x4f\xd3\xfb\x98\x0f\x6f\x4f\x5d\xf8\x67\x28\x6b\x55\x8b\x92\x92\x5b\x93\x3e\x4d\xae\x3e\x4f\xe6\xfb\x89\xb7\xf1\xbd\x02\xe7\x34\xa7\x6a\x92\xac\x51\x13\x2f\x99\x88\x4b\x94\x84\x1a\xde\xb8\xd9\x49\xcb\x65\xdc\x20\x5f\x35\xb4\x80\xab\xf9\xfc\x93\x9b\x87\x66\xd7\x4d\x98\xae\xb8\xe9\x04\xdb\x2e\xa0\x16\x78\x1f\xa6\x98\xe0\x2b\x19\x73\xc2\xd6\x2c\x20\x20\xfb\x85\x9d\xd7\xd9\x69\xb5\xd2\x68\x4c\xaf\xac\x53\x86\x13\x57\x72\xe1\x22\x8a\x11\x5f\xe3\x43\xb2\xa6\x63\xf2\xad\x0d\xac\x30\x4a\x58\xc2\x13\x22\x85\x50\xe5\x5d\x98\xf3\xd7\x78\x7c\x88\x52\x09\xa5\x17\xb0\x69\x78\xbf\x0d\xbc\x22\xe8\x34\xf6\xf0\xd0\xb1\xaa\xe2\x72\xb5\x80\xe7\x5d\x7f\x1e\x68\x99\x5e\x71\xb9\x80\xf9\x61\x4b\x96\xee\xcd\x98\xa5\x21\x63\x9d\x4d\xb2\x42\x55\x5b\xef\xc3\x8a\xaf\xa1\x14\xcc\x98\x3c\x3a\x31\xb1\xcf\x44\x47\x02\x2e\x01\x31\x2e\xf7\x4b\x47\x6b\x5a\x6d\x22\xf0\x8a\xf2\x28\x90\x88\x0b\x45\xa4\xda\x05\x5c\x39\x7a\xfd\x96\x13\x3c\x11\x8b\x55\x7c\xf5\x74\xbf\x38\xc9\x9a\xab\x3d\x08\xe1\x3d\xc5\xde\x3f\x83\x67\xa2\x65\xc6\xf7\x7b\x6b\x06\x35\x8b\x0b\x46\x4d\x04\x4c\x73\x16\x37\xbc\xaa\x50\xe6\x11\x69\x8b\x2e\x8e\xf8\x12\xc6\x79\x6f\x9f\xf6\x5e\x58\x6a\x50\xba\x73\x12\x56\x7d\x12\x84\x53\xd8\x15\xa7\xc6\x16\x31\x13\xf4\x28\x78\x96\x36\x57\xfb\x23\xa5\x15\x5f\xf7\x16\x19\xfd\x3c\x31\xce\xe3\xe7\xff\x1c\xfa\x1f\xaa\xae\x0d\x52\x3c\x32\xc7\x48\x98\xcb\xce\x52\xbc\xd2\xca\x76\xc3\xfa\x24\xf3\xb3\xc0\xab\x3c\x5a\x71\x43\x11\xd0\xb6\xeb\x6d\x17\x0d\x47\x52\xba\x8d\x9d\xeb\xb4\x12\x11\x74\x82\x95\xd8\x28\x51\xa1\xce\xa3\xde\x26\xaf\xb8\x21\xf8\xf1\x9f\xff\x80\xde\xc1\x5c\xae\x60\xab\xac\x86\x97\xd4\xa0\x46\xdb\x02\xab\x2a\x17\xdc\x49\x92\x8c\x74\xfb\x48\x7f\x9b\x5d\x5c\x90\x3c\x48\x4d\xb2\xc2\x12\xa9\x41\xb0\x20\x09\x05\xc9\xb8\xc2\x9a\x59\x31\x30\x0e\x42\x11\x28\x59\x0a\x5e\xde\xe5\x91\xb1\x45\xcb\x69\x3a\x8b\x96\xaf\xf8\x1a\xa1\xc5\x40\xe6\x49\x96\x06\xd1\x03\x8d\xd4\xf1\x18\x2c\x76\x70\xc0\x07\xfa\xe5\x24\x68\x49\x75\x0b\xb8\x7e\x3a\x8a\xd8\x87\x5c\xf6\xfc\xc4\x65\xd7\x0f\x0a\x77\x4c\xa2\x00\xff\x37\x36\x2d\x13\xfb\xdf\xfb\xb3\x1f\xce\x70\xba\x29\x76\xf7\x73\xa0\x36\xdc\xf3\xf9\x0d\xa8\x35\xea\x5a\xa8\xcd\x02\x98\x25\x75\x03\x2d\xbb\x1f\x72\xdd\xf5\x7c\x3e\xe6\xed\xea\x3f\x2b\x04\xfa\xf0\xd0\xf8\x9b\x45\x43\x66\x08\x8b\xb0\xe4\xff\xba\xe8\xa8\x50\x1a\xac\x4e\xac\xe1\x34\xba\x70\xf7\x52\x23\x8b\x1f\x6c\xfc\x10\xf7\x5a\xa9\x21\x7d\x8c\x69\xf4\xd0\xa3\x4c\x17\x2d\x33\xd2\x07\xb9\x49\x46\xd5\x1f\xba\xfe\xda\x95\xf7\xc7\x6e\x7f\x88\x4f\x77\xf6\x0e\x51\x87\xda\xe2\x22\x05\xfc\x30\x4b\xa9\xfa\x08\xcd\x15\x23\x56\x30\x83\x1f\xa2\xde\x67\xf9\x83\x7a\x3f\xfc\x58\xfd\x0d\x32\x4d\x05\xb2\xc7\x13\xd4\x88\x40\x6d\x65\x35\x3a\xbf\xbf\x48\x1f\x4b\xc0\x4a\xbe\x46\x6d\x38\x6d\x3f\x94\x01\x56\x07\x0a\x61\x7c\x4c\x21\x4b\x49\xbf\x3b\xd6\xfe\x0f\x97\xfb\x7d\xe5\xe8\x7a\xf9\xb5\xda\x40\xa5\xd0\x00\x35\xdc\x80\x2b\x26\x5f\x64\x69\x73\x3d\x88\x74\xcb\xd7\x6e\xc1\x1b\x15\xea\x50\x4f\xb8\x01\x6d\xa5\xcf\xa3\x4a\x02\x35\x78\x5c\x8a\x64\xf8\x95\xc0\x6b\xe5\xca\xf9\x1a\x25\x41\xcb\x04\x2f\xb9\xb2\x06\x58\x49\x4a\x1b\xa8\xb5\x6a\x01\xef\x1b\x66\x0d\x39\x20\x97\x3e\xd8\x9a\x71\xe1\xef\x92\x77\x29\x28\x0d\xac\x2c\x6d\x6b\x5d\x3b\x22\x57\x80\x52\xd9\x55\xd3\x73\x21\x05\xad\xb2\x92\x40\x28\xb9\x1a\xf8\x98\x8e\xb5\xc0\x88\x58\x79\x67\x2e\x61\x9f\x15\x80\x69\x04\xe2\x58\xb9\x5d\x7d\x55\x60\x65\xe9\xb6\x9b\x04\x5e\xc8\xad\x92\x08\x0d\x5b\x7b\x22\x27\x02\xd0\xb2\xed\x1e\xa8\xe7\xb5\xe1\xd4\xf0\x70\xf0\x0e\x75\xeb\xfa\xcb\x0a\x04\x6f\x39\x81\xaa\x21\x33\xa4\x95\x5c\xb9\x67\xc9\x0b\xcf\x70\xb7\x0b\x94\xa7\x66\x06\xa9\x33\xd5\xf7\xa8\xb9\xaa\x76\x3b\xd7\xba\x78\xd1\x24\x4b\xbb\xb1\xc5\xd5\xb1\xc2\x4b\x30\xbc\xed\xc4\x16\x4a\x8d\x8c\x10\x18\x64\xec\xe4\x41\xe1\xca\x63\x12\xea\xba\x6f\x49\x23\x20\xa6\x57\xee\xb9\xf6\x1f\x56\x28\x4b\x8b\x42\x30\x79\xe7\xaa\xcd\x50\x12\xb3\x94\x2d\xfd\x51\x1e\x2e\x86\xd0\x31\xe3\xce\xc5\x25\x29\x7f\xd4\xfe\x7d\x66\x60\xea\x46\x35\x17\xe8\x9f\x70\x3e\x7a\xe4\xb9\xb3\x93\xeb\xb3\x67\x97\x50\xaa\x6e\x1b\x76\xfb\x7d\x8e\x9a\xf1\xf5\x77\x80\x62\x85\x5a\x23\x84\xe2\x5e\xa8\x7b\x60\xb2\x82\x9a\x6b\x04\xb6\x61\xdb\x27\xf0\x93\xb2\x50\x32\x09\xa4\x59\x79\x17\x74\x5b\xad\x5d\x18\x75\x28\x5d\xa9\x38\x38\xb6\x40\xa1\x36\x5e\x24\xa0\xd5\x1c\x85\xf7\xb2\x41\x84\x46\x6d\xa0\xb5\xa5\x3f\xa0\x73\x2f\xba\x85\x0d\xe3\x04\x56\x12\x17\xe1\xdc\x64\xb5\x84\x52\xb5\x68\x46\x5e\x78\xf0\xfa\x0d\xbf\xfa\x1f\x87\x37\x82\x5f\x4e\x53\x78\x25\x54\xc1\x04\xac\x5d\xc6\x28\x84\xbb\x54\x0a\x5c\x33\x72\x74\x06\x43\x8c\xac\x71\x91\xe2\xed\xe8\xaf\x94\xdb\xbf\x66\xda\x45\x2e\xb6\x1d\x41\xde\x77\xb8\x6e\xce\xa0\x5e\xbb\xbe\xbd\xd7\xf1\x15\xd6\x5c\x06\xcb\xd6\x56\x96\xae\x01\x07\x6a\x18\x41\x68\x29\x0c\x30\x6f\x71\xb0\x5a\x40\x6f\xee\x80\x30\xe0\x79\x39\xc8\x87\xed\xd3\x59\xdf\x71\x07\xb9\xc4\xa0\xac\xa6\x7f\xff\xe1\xbb\x6f\x13\x43\x9a\xcb\x15\xaf\xb7\xd3\x37\x56\x8b\x05\xfc\x75\x1a\xfd\xc5\x37\x62\xb3\x9f\xe7\xbf\x26\x6b\x26\x2c\xee\x66\xb3\xf0\x4c\xb8\x39\xe6\xc7\xa0\x45\x6a\x94\xf7\x85\xc6\x52\x49\x89\x25\x81\xed\x94\xec\xe9\x80\x50\xc6\xec\x39\x1d\x24\x1e\xa0\xc5\x6b\x98\xee\x0d\xf3\x09\x3c\x85\x3c\x87\xf9\x7e\xad\xe7\x0c\x39\x48\xdc\xc0\xbf\xb0\xf8\x41\x95\x77\x48\xd3\x68\x63\xdc\xb5\x88\xe0\x02\x84\x2a\x99\xc3\x4b\x1a\x65\x08\x2e\x20\x4a\x59\xc7\xa3\xc0\x7a\xb2\x03\x14\x06\xdf\x0f\xf6\x41\x58\xe1\xcd\x15\x98\x5e\x5c\x04\x8f\xed\x8d\xaa\x64\x8b\xc6\xb0\x15\x8e\x4f\xe8\x73\xe3\x70\x14\x67\x88\xd6\xac\x20\x07\x6f\xfc\x8e\x69\x83\x41\x24\x71\xf5\xb8\xd7\xe2\xcd\xe1\xc5\xf2\x1c\xa4\x15\x62\xd8\x3f\xd1\xe8\x82\xb9\x17\xdb\x9d\x1d\x89\x27\x21\x75\x3d\xc9\x73\x70\xc5\xc9\xf9\xa8\x3a\xec\x74\x8e\x0d\x65\x74\x96\xb8\xfa\x78\xd8\x31\x1b\xe0\xde\x42\xc3\xea\x7d\x70\x58\x9d\xe2\x61\xf5\x08\xa0\xef\x5a\xde\x85\x17\xba\x9c\x11\x9c\x9f\x78\x04\x4d\xda\xb6\x40\xfd\x2e\xb8\xd0\xb5\xf4\x70\xde\xd4\xdf\x48\x1a\xed\xbd\x84\xab\xe7\xb3\x47\xd0\x51\x6b\xf5\x28\xb8\x54\xb4\x9d\xbe\x11\x6c\xeb\xb2\x2e\x9c\x93\xea\xbe\xf4\x4d\xc6\xf9\x25\x38\x5d\x0b\x18\x10\x2e\xfd\xe3\x60\x01\xe7\x7e\x74\xbe\x7b\x44\x9b\xb1\x65\xe9\xf2\xf1\xc7\xe8\xeb\x31\x06\x8d\xfd\xf8\x51\x9d\x43\x7e\x3d\x52\x0a\x9f\x7e\x0a\x6f\xad\x1e\x87\xa0\x8b\xe1\xbe\x50\x40\x0e\x51\xd4\xc3\x4f\x6a\xa5\x61\xea\x16\x79\x3e\xbf\x01\x9e\x8d\x61\x12\x81\x72\x45\xcd\x0d\xf0\x8b\x8b\x03\xd2\x64\x0f\x73\x91\x43\xe4\xfa\xe8\x8c\xaa\xa5\xef\x67\x42\xd3\xf3\x4b\x54\xb0\xf2\xce\x3d\xc9\x64\xb5\x70\xd9\x6e\x7a\x7e\x28\x86\xa3\x3a\x78\x71\x44\xf9\x67\xfe\x6b\x62\x0d\x6a\x5f\xb9\x2e\x20\x4a\x3a\xb9\xfa\xc2\xf0\xdf\x31\x7f\xfe\xec\x7c\x76\x03\x07\xcc\xd8\xcd\x2e\xa0\x74\x2f\x92\x1b\x08\x5d\xbd\xef\xad\x60\x78\x8f\xf8\x51\xa1\x74\x85\x3a\xd6\xac\xe2\xd6\x2c\xe0\x59\x77\x7f\xf3\x8b\xeb\x04\x5d\x89\xf0\x1d\xa0\xe7\xdd\x69\x5c\x3e\xc4\x65\xdf\x64\x5c\x40\x94\xa5\x4e\x68\xbf\x65\x38\xe5\xf8\xcb\x09\x3c\xd0\xbb\xc2\xf0\x5d\xa3\x9f\x6f\x79\x55\x09\x74\x24\xbc\xc2\xf0\x01\xaa\xb2\xda\x27\xae\x69\x18\x4f\x4f\x79\x10\x6f\x71\x96\x58\xc9\xef\xa7\xb3\xb8\x97\xd9\x8f\x2f\xe1\xdc\xb8\xfc\x5c\x99\xf3\x59\xd2\xd8\x96\x49\xfe\x3b\x4e\x5d\x23\x3c\x0b\xbc\x1d\x63\xd7\xdd\x0e\xde\xde\x8d\x2e\xda\xf0\x32\x9b\x25\x0d\xb5\x62\x1a\x65\xe4\xbf\xce\x38\x72\x83\x8b\x3d\x4a\x98\x3e\x8e\xc8\xdd\x71\x0e\x2d\x85\x32\x78\x52\x23\xc0\x20\xbd\xe6\x2d\x2a\x4b\xd3\xa1\x8e\x5c\xba\xd7\xe2\x7c\x76\x03\xa1\x2e\x1d\x10\xc2\xdd\xfd\xe3\x08\xbb\xbe\xbc\xbd\x34\xae\x83\xe7\xa6\x01\x06\x1b\x2c\x8c\xaf\x10\xd0\xef\xf1\xb5\x38\xd4\xdc\x17\xdf\x7f\x33\xaa\xbb\x03\xea\xd4\x1f\x6f\xf4\x99\x31\x4b\xc3\xb7\xaa\x2c\x0d\xdf\xe1\xff\x1b\x00\x00\xff\xff\x97\x3f\x1d\xc4\x98\x17\x00\x00") + +func faucetHtmlBytes() ([]byte, error) { + return bindataRead( + _faucetHtml, + "faucet.html", + ) +} + +func faucetHtml() (*asset, error) { + bytes, err := faucetHtmlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "faucet.html", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "faucet.html": faucetHtml, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} +var _bintree = &bintree{nil, map[string]*bintree{ + "faucet.html": &bintree{faucetHtml, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} + diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go index f86be62ba..90f79a47e 100644 --- a/cmd/geth/accountcmd.go +++ b/cmd/geth/accountcmd.go @@ -179,8 +179,7 @@ nodes. ) func accountList(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) - + stack, _ := makeConfigNode(ctx) var index int for _, wallet := range stack.AccountManager().Wallets() { for _, account := range wallet.Accounts() { @@ -278,7 +277,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr // accountCreate creates a new account into the keystore defined by the CLI flags. func accountCreate(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + stack, _ := makeConfigNode(ctx) password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) @@ -296,7 +295,7 @@ func accountUpdate(ctx *cli.Context) error { if len(ctx.Args()) == 0 { utils.Fatalf("No accounts specified to update") } - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + stack, _ := makeConfigNode(ctx) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil) @@ -317,7 +316,7 @@ func importWallet(ctx *cli.Context) error { utils.Fatalf("Could not read wallet file: %v", err) } - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + stack, _ := makeConfigNode(ctx) passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx)) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) @@ -338,7 +337,7 @@ func accountImport(ctx *cli.Context) error { if err != nil { utils.Fatalf("Failed to load the private key: %v", err) } - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + stack, _ := makeConfigNode(ctx) passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 6ea474a9c..66516b409 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -17,6 +17,7 @@ package main import ( + "encoding/json" "fmt" "os" "runtime" @@ -110,17 +111,22 @@ func initGenesis(ctx *cli.Context) error { stack := makeFullNode(ctx) chaindb := utils.MakeChainDatabase(ctx, stack) - genesisFile, err := os.Open(genesisPath) + file, err := os.Open(genesisPath) if err != nil { utils.Fatalf("failed to read genesis file: %v", err) } - defer genesisFile.Close() + defer file.Close() - block, err := core.WriteGenesisBlock(chaindb, genesisFile) + genesis := new(core.Genesis) + if err := json.NewDecoder(file).Decode(genesis); err != nil { + utils.Fatalf("invalid genesis file: %v", err) + } + + _, hash, err := core.SetupGenesisBlock(chaindb, genesis) if err != nil { utils.Fatalf("failed to write genesis block: %v", err) } - log.Info("Successfully wrote genesis state", "hash", block.Hash()) + log.Info("Successfully wrote genesis state", "hash", hash) return nil } @@ -238,7 +244,7 @@ func exportChain(ctx *cli.Context) error { } func removeDB(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + stack, _ := makeConfigNode(ctx) dbdir := stack.ResolvePath(utils.ChainDbName(ctx)) if !common.FileExist(dbdir) { fmt.Println(dbdir, "does not exist") diff --git a/cmd/geth/config.go b/cmd/geth/config.go new file mode 100644 index 000000000..86dd4bfdf --- /dev/null +++ b/cmd/geth/config.go @@ -0,0 +1,186 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bufio" + "encoding/hex" + "errors" + "fmt" + "io" + "os" + "reflect" + "unicode" + + cli "gopkg.in/urfave/cli.v1" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/contracts/release" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" + "github.com/naoina/toml" +) + +var ( + dumpConfigCommand = cli.Command{ + Action: dumpConfig, + Name: "dumpconfig", + Usage: "Show configuration values", + ArgsUsage: "", + Category: "MISCELLANEOUS COMMANDS", + Description: `The dumpconfig command shows configuration values.`, + } + + configFileFlag = cli.StringFlag{ + Name: "config", + Usage: "TOML configuration file", + } +) + +// These settings ensure that TOML keys use the same names as Go struct fields. +var tomlSettings = toml.Config{ + NormFieldName: func(rt reflect.Type, key string) string { + return key + }, + FieldToKey: func(rt reflect.Type, field string) string { + return field + }, + MissingField: func(rt reflect.Type, field string) error { + link := "" + if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" { + link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name()) + } + return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link) + }, +} + +type ethstatsConfig struct { + URL string `toml:",omitempty"` +} + +type gethConfig struct { + Eth eth.Config + Node node.Config + Ethstats ethstatsConfig +} + +func loadConfig(file string, cfg *gethConfig) error { + f, err := os.Open(file) + if err != nil { + return err + } + defer f.Close() + + err = tomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg) + // Add file name to errors that have a line number. + if _, ok := err.(*toml.LineError); ok { + err = errors.New(file + ", " + err.Error()) + } + return err +} + +func defaultNodeConfig() node.Config { + cfg := node.DefaultConfig + cfg.Name = clientIdentifier + cfg.Version = params.VersionWithCommit(gitCommit) + cfg.HTTPModules = append(cfg.HTTPModules, "eth") + cfg.WSModules = append(cfg.WSModules, "eth") + cfg.IPCPath = "geth.ipc" + return cfg +} + +func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { + // Load defaults. + cfg := gethConfig{ + Eth: eth.DefaultConfig, + Node: defaultNodeConfig(), + } + + // Load config file. + if file := ctx.GlobalString(configFileFlag.Name); file != "" { + if err := loadConfig(file, &cfg); err != nil { + utils.Fatalf("%v", err) + } + } + + // Apply flags. + utils.SetNodeConfig(ctx, &cfg.Node) + stack, err := node.New(&cfg.Node) + if err != nil { + utils.Fatalf("Failed to create the protocol stack: %v", err) + } + utils.SetEthConfig(ctx, stack, &cfg.Eth) + if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) { + cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) + } + + return stack, cfg +} + +func makeFullNode(ctx *cli.Context) *node.Node { + stack, cfg := makeConfigNode(ctx) + + utils.RegisterEthService(stack, &cfg.Eth) + + // Whisper must be explicitly enabled, but is auto-enabled in --dev mode. + shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name) + shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name) + if shhEnabled || shhAutoEnabled { + utils.RegisterShhService(stack) + } + + // Add the Ethereum Stats daemon if requested. + if cfg.Ethstats.URL != "" { + utils.RegisterEthStatsService(stack, cfg.Ethstats.URL) + } + + // Add the release oracle service so it boots along with node. + if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + config := release.Config{ + Oracle: relOracle, + Major: uint32(params.VersionMajor), + Minor: uint32(params.VersionMinor), + Patch: uint32(params.VersionPatch), + } + commit, _ := hex.DecodeString(gitCommit) + copy(config.Commit[:], commit) + return release.NewReleaseService(ctx, config) + }); err != nil { + utils.Fatalf("Failed to register the Geth release oracle service: %v", err) + } + return stack +} + +// dumpConfig is the dumpconfig command. +func dumpConfig(ctx *cli.Context) error { + _, cfg := makeConfigNode(ctx) + comment := "" + + if cfg.Eth.Genesis != nil { + cfg.Eth.Genesis = nil + comment += "# Note: this config doesn't contain the genesis block.\n\n" + } + + out, err := tomlSettings.Marshal(&cfg) + if err != nil { + return err + } + io.WriteString(os.Stdout, comment) + os.Stdout.Write(out) + return nil +} diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index 820e9d082..e5472836c 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -22,14 +22,17 @@ import ( "os" "path/filepath" "runtime" - "sort" "strconv" "strings" "testing" "time" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" +) + +const ( + ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0" + httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0" ) // Tests that a node embedded within a console can be started up properly and @@ -45,24 +48,21 @@ func TestConsoleWelcome(t *testing.T) { // Gather all the infos the welcome message needs to contain geth.setTemplateFunc("goos", func() string { return runtime.GOOS }) + geth.setTemplateFunc("goarch", func() string { return runtime.GOARCH }) geth.setTemplateFunc("gover", runtime.Version) geth.setTemplateFunc("gethver", func() string { return params.Version }) geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) }) - geth.setTemplateFunc("apis", func() []string { - apis := append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi) - sort.Strings(apis) - return apis - }) + geth.setTemplateFunc("apis", func() string { return ipcAPIs }) // Verify the actual welcome message to the required template geth.expect(` Welcome to the Geth JavaScript console! -instance: Geth/v{{gethver}}/{{goos}}/{{gover}} +instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}} coinbase: {{.Etherbase}} at block: 0 ({{niltime}}) datadir: {{.Datadir}} - modules:{{range apis}} {{.}}:1.0{{end}} + modules: {{apis}} > {{.InputLine "exit"}} `) @@ -88,7 +88,7 @@ func TestIPCAttachWelcome(t *testing.T) { "--etherbase", coinbase, "--shh", "--ipcpath", ipc) time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open - testAttachWelcome(t, geth, "ipc:"+ipc) + testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs) geth.interrupt() geth.expectExit() @@ -102,7 +102,7 @@ func TestHTTPAttachWelcome(t *testing.T) { "--etherbase", coinbase, "--rpc", "--rpcport", port) time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open - testAttachWelcome(t, geth, "http://localhost:"+port) + testAttachWelcome(t, geth, "http://localhost:"+port, httpAPIs) geth.interrupt() geth.expectExit() @@ -117,13 +117,13 @@ func TestWSAttachWelcome(t *testing.T) { "--etherbase", coinbase, "--ws", "--wsport", port) time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open - testAttachWelcome(t, geth, "ws://localhost:"+port) + testAttachWelcome(t, geth, "ws://localhost:"+port, httpAPIs) geth.interrupt() geth.expectExit() } -func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) { +func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) { // Attach to a running geth note and terminate immediately attach := runGeth(t, "attach", endpoint) defer attach.expectExit() @@ -131,32 +131,24 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) { // Gather all the infos the welcome message needs to contain attach.setTemplateFunc("goos", func() string { return runtime.GOOS }) + attach.setTemplateFunc("goarch", func() string { return runtime.GOARCH }) attach.setTemplateFunc("gover", runtime.Version) attach.setTemplateFunc("gethver", func() string { return params.Version }) attach.setTemplateFunc("etherbase", func() string { return geth.Etherbase }) attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) }) attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") }) attach.setTemplateFunc("datadir", func() string { return geth.Datadir }) - attach.setTemplateFunc("apis", func() []string { - var apis []string - if strings.HasPrefix(endpoint, "ipc") { - apis = append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi) - } else { - apis = append(strings.Split(rpc.DefaultHTTPApis, ","), rpc.MetadataApi) - } - sort.Strings(apis) - return apis - }) + attach.setTemplateFunc("apis", func() string { return apis }) // Verify the actual welcome message to the required template attach.expect(` Welcome to the Geth JavaScript console! -instance: Geth/v{{gethver}}/{{goos}}/{{gover}} +instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}} coinbase: {{etherbase}} at block: 0 ({{niltime}}){{if ipc}} datadir: {{datadir}}{{end}} - modules:{{range apis}} {{.}}:1.0{{end}} + modules: {{apis}} > {{.InputLine "exit" }} `) diff --git a/cmd/geth/dao_test.go b/cmd/geth/dao_test.go index f9ce80218..ec7802ada 100644 --- a/cmd/geth/dao_test.go +++ b/cmd/geth/dao_test.go @@ -84,27 +84,24 @@ var daoGenesisForkBlock = big.NewInt(314) // set in the database after various initialization procedures and invocations. func TestDAOForkBlockNewChain(t *testing.T) { for i, arg := range []struct { - testnet bool genesis string expectBlock *big.Int expectVote bool }{ // Test DAO Default Mainnet - {false, "", params.MainNetDAOForkBlock, true}, - // test DAO Default Testnet - {true, "", params.TestNetDAOForkBlock, true}, + {"", params.MainNetDAOForkBlock, true}, // test DAO Init Old Privnet - {false, daoOldGenesis, nil, false}, + {daoOldGenesis, nil, false}, // test DAO Default No Fork Privnet - {false, daoNoForkGenesis, daoGenesisForkBlock, false}, + {daoNoForkGenesis, daoGenesisForkBlock, false}, // test DAO Default Pro Fork Privnet - {false, daoProForkGenesis, daoGenesisForkBlock, true}, + {daoProForkGenesis, daoGenesisForkBlock, true}, } { - testDAOForkBlockNewChain(t, i, arg.testnet, arg.genesis, arg.expectBlock, arg.expectVote) + testDAOForkBlockNewChain(t, i, arg.genesis, arg.expectBlock, arg.expectVote) } } -func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis string, expectBlock *big.Int, expectVote bool) { +func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBlock *big.Int, expectVote bool) { // Create a temporary data directory to use and inspect later datadir := tmpdir(t) defer os.RemoveAll(datadir) @@ -119,17 +116,11 @@ func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis stri } else { // Force chain initialization args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir} - if testnet { - args = append(args, "--testnet") - } geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...) geth.cmd.Wait() } // Retrieve the DAO config flag from the database path := filepath.Join(datadir, "geth", "chaindata") - if testnet && genesis == "" { - path = filepath.Join(datadir, "testnet", "geth", "chaindata") - } db, err := ethdb.NewLDBDatabase(path, 0, 0) if err != nil { t.Fatalf("test %d: failed to open test database: %v", test, err) @@ -137,9 +128,6 @@ func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis stri defer db.Close() genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - if testnet { - genesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") - } if genesis != "" { genesisHash = daoGenesisHash } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index cc6d3ac6a..5fb50c4ad 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -18,7 +18,6 @@ package main import ( - "encoding/hex" "fmt" "os" "runtime" @@ -29,17 +28,13 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/console" - "github.com/ethereum/go-ethereum/contracts/release" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" "gopkg.in/urfave/cli.v1" ) @@ -82,6 +77,8 @@ func init() { versionCommand, bugCommand, licenseCommand, + // See config.go + dumpConfigCommand, } app.Flags = []cli.Flag{ @@ -99,6 +96,7 @@ func init() { utils.EthashDatasetsOnDiskFlag, utils.FastSyncFlag, utils.LightModeFlag, + utils.SyncModeFlag, utils.LightServFlag, utils.LightPeersFlag, utils.LightKDFFlag, @@ -129,16 +127,12 @@ func init() { utils.WSApiFlag, utils.WSAllowedOriginsFlag, utils.IPCDisabledFlag, - utils.IPCApiFlag, utils.IPCPathFlag, utils.ExecFlag, utils.PreloadJSFlag, utils.WhisperEnabledFlag, utils.DevModeFlag, utils.TestNetFlag, - utils.VMForceJitFlag, - utils.VMJitCacheFlag, - utils.VMEnableJitFlag, utils.VMEnableDebugFlag, utils.NetworkIdFlag, utils.RPCCORSDomainFlag, @@ -146,14 +140,10 @@ func init() { utils.MetricsEnabledFlag, utils.FakePoWFlag, utils.NoCompactionFlag, - utils.SolcPathFlag, - utils.GpoMinGasPriceFlag, - utils.GpoMaxGasPriceFlag, - utils.GpoFullBlockRatioFlag, - utils.GpobaseStepDownFlag, - utils.GpobaseStepUpFlag, - utils.GpobaseCorrectionFactorFlag, + utils.GpoBlocksFlag, + utils.GpoPercentileFlag, utils.ExtraDataFlag, + configFileFlag, } app.Flags = append(app.Flags, debug.Flags...) @@ -165,12 +155,6 @@ func init() { // Start system runtime metrics collection go metrics.CollectProcessMetrics(3 * time.Second) - // This should be the only place where reporting is enabled - // because it is not intended to run while testing. - // In addition to this check, bad block reports are sent only - // for chains with the main network genesis block and network id 1. - eth.EnableBadBlockReporting = true - utils.SetupNetwork(ctx) return nil } @@ -199,52 +183,6 @@ func geth(ctx *cli.Context) error { return nil } -func makeFullNode(ctx *cli.Context) *node.Node { - // Create the default extradata and construct the base node - var clientInfo = struct { - Version uint - Name string - GoVersion string - Os string - }{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS} - extra, err := rlp.EncodeToBytes(clientInfo) - if err != nil { - log.Warn("Failed to set canonical miner information", "err", err) - } - if uint64(len(extra)) > params.MaximumExtraDataSize { - log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize) - extra = nil - } - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) - utils.RegisterEthService(ctx, stack, extra) - - // Whisper must be explicitly enabled, but is auto-enabled in --dev mode. - shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name) - shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name) - if shhEnabled || shhAutoEnabled { - utils.RegisterShhService(stack) - } - // Add the Ethereum Stats daemon if requested - if url := ctx.GlobalString(utils.EthStatsURLFlag.Name); url != "" { - utils.RegisterEthStatsService(stack, url) - } - // Add the release oracle service so it boots along with node. - if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - config := release.Config{ - Oracle: relOracle, - Major: uint32(params.VersionMajor), - Minor: uint32(params.VersionMinor), - Patch: uint32(params.VersionPatch), - } - commit, _ := hex.DecodeString(gitCommit) - copy(config.Commit[:], commit) - return release.NewReleaseService(ctx, config) - }); err != nil { - utils.Fatalf("Failed to register the Geth release oracle service: %v", err) - } - return stack -} - // startNode boots up the system node and all registered protocols, after which // it unlocks any requested accounts, and starts the RPC/IPC interfaces and the // miner. @@ -303,7 +241,15 @@ func startNode(ctx *cli.Context, stack *node.Node) { if err := stack.Service(ðereum); err != nil { utils.Fatalf("ethereum service not running: %v", err) } - if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil { + if threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name); threads > 0 { + type threaded interface { + SetThreads(threads int) + } + if th, ok := ethereum.Engine().(threaded); ok { + th.SetThreads(threads) + } + } + if err := ethereum.StartMining(true); err != nil { utils.Fatalf("Failed to start mining: %v", err) } } diff --git a/cmd/geth/misccmd.go b/cmd/geth/misccmd.go index 073c36beb..cb7dc1673 100644 --- a/cmd/geth/misccmd.go +++ b/cmd/geth/misccmd.go @@ -26,9 +26,9 @@ import ( "strings" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "gopkg.in/urfave/cli.v1" ) @@ -87,7 +87,7 @@ func makedag(ctx *cli.Context) error { utils.Fatalf("Can't find dir") } fmt.Println("making DAG, this could take awhile...") - pow.MakeDataset(blockNum, dir) + ethash.MakeDataset(blockNum, dir) } default: wrongArgs() @@ -101,10 +101,11 @@ func version(ctx *cli.Context) error { if gitCommit != "" { fmt.Println("Git Commit:", gitCommit) } + fmt.Println("Architecture:", runtime.GOARCH) fmt.Println("Protocol Versions:", eth.ProtocolVersions) fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name)) fmt.Println("Go Version:", runtime.Version()) - fmt.Println("OS:", runtime.GOOS) + fmt.Println("Operating System:", runtime.GOOS) fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH")) fmt.Printf("GOROOT=%s\n", runtime.GOROOT()) return nil diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 74768f507..a172b4775 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -115,7 +115,6 @@ var AppHelpFlagGroups = []flagGroup{ utils.WSApiFlag, utils.WSAllowedOriginsFlag, utils.IPCDisabledFlag, - utils.IPCApiFlag, utils.IPCPathFlag, utils.RPCCORSDomainFlag, utils.JSpathFlag, @@ -151,20 +150,13 @@ var AppHelpFlagGroups = []flagGroup{ { Name: "GAS PRICE ORACLE", Flags: []cli.Flag{ - utils.GpoMinGasPriceFlag, - utils.GpoMaxGasPriceFlag, - utils.GpoFullBlockRatioFlag, - utils.GpobaseStepDownFlag, - utils.GpobaseStepUpFlag, - utils.GpobaseCorrectionFactorFlag, + utils.GpoBlocksFlag, + utils.GpoPercentileFlag, }, }, { Name: "VIRTUAL MACHINE", Flags: []cli.Flag{ - utils.VMEnableJitFlag, - utils.VMForceJitFlag, - utils.VMJitCacheFlag, utils.VMEnableDebugFlag, }, }, @@ -182,12 +174,6 @@ var AppHelpFlagGroups = []flagGroup{ utils.WhisperEnabledFlag, }, }, - { - Name: "MISCELLANEOUS", - Flags: []cli.Flag{ - utils.SolcPathFlag, - }, - }, } func init() { diff --git a/cmd/puppeth/module.go b/cmd/puppeth/module.go new file mode 100644 index 000000000..b6a029a01 --- /dev/null +++ b/cmd/puppeth/module.go @@ -0,0 +1,152 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "encoding/json" + "errors" + "fmt" + "net" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/log" +) + +var ( + // ErrServiceUnknown is returned when a service container doesn't exist. + ErrServiceUnknown = errors.New("service unknown") + + // ErrServiceOffline is returned when a service container exists, but it is not + // running. + ErrServiceOffline = errors.New("service offline") + + // ErrServiceUnreachable is returned when a service container is running, but + // seems to not respond to communication attempts. + ErrServiceUnreachable = errors.New("service unreachable") + + // ErrNotExposed is returned if a web-service doesn't have an exposed port, nor + // a reverse-proxy in front of it to forward requests. + ErrNotExposed = errors.New("service not exposed, nor proxied") +) + +// containerInfos is a heavily reduced version of the huge inspection dataset +// returned from docker inspect, parsed into a form easily usable by puppeth. +type containerInfos struct { + running bool // Flag whether the container is running currently + envvars map[string]string // Collection of environmental variables set on the container + portmap map[string]int // Port mapping from internal port/proto combos to host binds + volumes map[string]string // Volume mount points from container to host directories +} + +// inspectContainer runs docker inspect against a running container +func inspectContainer(client *sshClient, container string) (*containerInfos, error) { + // Check whether there's a container running for the service + out, err := client.Run(fmt.Sprintf("docker inspect %s", container)) + if err != nil { + return nil, ErrServiceUnknown + } + // If yes, extract various configuration options + type inspection struct { + State struct { + Running bool + } + Mounts []struct { + Source string + Destination string + } + Config struct { + Env []string + } + HostConfig struct { + PortBindings map[string][]map[string]string + } + } + var inspects []inspection + if err = json.Unmarshal(out, &inspects); err != nil { + return nil, err + } + inspect := inspects[0] + + // Infos retrieved, parse the above into something meaningful + infos := &containerInfos{ + running: inspect.State.Running, + envvars: make(map[string]string), + portmap: make(map[string]int), + volumes: make(map[string]string), + } + for _, envvar := range inspect.Config.Env { + if parts := strings.Split(envvar, "="); len(parts) == 2 { + infos.envvars[parts[0]] = parts[1] + } + } + for portname, details := range inspect.HostConfig.PortBindings { + if len(details) > 0 { + port, _ := strconv.Atoi(details[0]["HostPort"]) + infos.portmap[portname] = port + } + } + for _, mount := range inspect.Mounts { + infos.volumes[mount.Destination] = mount.Source + } + return infos, err +} + +// tearDown connects to a remote machine via SSH and terminates docker containers +// running with the specified name in the specified network. +func tearDown(client *sshClient, network string, service string, purge bool) ([]byte, error) { + // Tear down the running (or paused) container + out, err := client.Run(fmt.Sprintf("docker rm -f %s_%s_1", network, service)) + if err != nil { + return out, err + } + // If requested, purge the associated docker image too + if purge { + return client.Run(fmt.Sprintf("docker rmi %s/%s", network, service)) + } + return nil, nil +} + +// resolve retrieves the hostname a service is running on either by returning the +// actual server name and port, or preferably an nginx virtual host if available. +func resolve(client *sshClient, network string, service string, port int) (string, error) { + // Inspect the service to get various configurations from it + infos, err := inspectContainer(client, fmt.Sprintf("%s_%s_1", network, service)) + if err != nil { + return "", err + } + if !infos.running { + return "", ErrServiceOffline + } + // Container online, extract any environmental variables + if vhost := infos.envvars["VIRTUAL_HOST"]; vhost != "" { + return vhost, nil + } + return fmt.Sprintf("%s:%d", client.server, port), nil +} + +// checkPort tries to connect to a remote host on a given +func checkPort(host string, port int) error { + log.Trace("Verifying remote TCP connectivity", "server", host, "port", port) + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), time.Second) + if err != nil { + return err + } + conn.Close() + return nil +} diff --git a/cmd/puppeth/module_dashboard.go b/cmd/puppeth/module_dashboard.go new file mode 100644 index 000000000..17f119111 --- /dev/null +++ b/cmd/puppeth/module_dashboard.go @@ -0,0 +1,537 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bytes" + "fmt" + "html/template" + "math/rand" + "path/filepath" + "strings" + + "github.com/ethereum/go-ethereum/log" +) + +// dashboardContent is the actual dashboard HTML content to serve up when users +// load the dashboard website. +var dashboardContent = ` +<!DOCTYPE html> +<html lang="en"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <!-- Meta, title, CSS, favicons, etc. --> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <title>{{.NetworkTitle}}: Ethereum Testnet</title> + + <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> + <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> + <link href="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/css/custom.min.css" rel="stylesheet"> + <style> + .vertical-center { + min-height: 100%; + min-height: 95vh; + display: flex; + align-items: center; + } + .nav.side-menu li a { + font-size: 18px; + } + .nav-sm .nav.side-menu li a { + font-size: 10px; + } + pre{ + white-space: pre-wrap; + } + </style> + </head> + + <body class="nav-sm" style="overflow-x: hidden"> + <div class="container body"> + <div class="main_container"> + <div class="col-md-3 left_col"> + <div class="left_col scroll-view"> + <div class="navbar nav_title" style="border: 0; margin-top: 8px;"> + <a class="site_title"><i class="fa fa-globe" style="margin-left: 6px"></i> <span>{{.NetworkTitle}} Testnet</span></a> + </div> + <div class="clearfix"></div> + <br /> + <div id="sidebar-menu" class="main_menu_side hidden-print main_menu"> + <div class="menu_section"> + <ul class="nav side-menu"> + {{if .EthstatsPage}}<li><a onclick="load('//{{.EthstatsPage}}')"><i class="fa fa-tachometer"></i> Network Stats</a></li>{{end}} + {{if .ExplorerPage}}<li><a onclick="load('//{{.ExplorerPage}}')"><i class="fa fa-database"></i> Block Explorer</a></li>{{end}} + {{if .WalletPage}}<li><a onclick="load('//{{.WalletPage}}')"><i class="fa fa-address-book-o"></i> Browser Wallet</a></li>{{end}} + {{if .FaucetPage}}<li><a onclick="load('//{{.FaucetPage}}')"><i class="fa fa-bath"></i> Crypto Faucet</a></li>{{end}} + <li id="connect"><a><i class="fa fa-plug"></i> Connect Yourself</a> + <ul id="connect_list" class="nav child_menu"> + <li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-geth')">Go Ethereum: Geth</a></li> + <li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-mist')">Go Ethereum: Wallet & Mist</a></li> + <li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-mobile')">Go Ethereum: Android & iOS</a></li> + </ul> + </li> + <li><a onclick="load('#about')"><i class="fa fa-heartbeat"></i> About Puppeth</a></li> + </ul> + </div> + </div> + </div> + </div> + <div class="right_col" role="main" style="padding: 0"> + <div id="connect-go-ethereum-geth" hidden style="padding: 16px;"> + <div class="page-title"> + <div class="title_left"> + <h3>Connect Yourself – Go Ethereum: Geth</h3> + </div> + </div> + <div class="clearfix"></div> + <div class="row"> + <div class="col-md-6"> + <div class="x_panel"> + <div class="x_title"> + <h2><i class="fa fa-archive" aria-hidden="true"></i> Archive node <small>Retains all historical data</small></h2> + <div class="clearfix"></div> + </div> + <div class="x_content"> + <p>An archive node synchronizes the blockchain by downloading the full chain from the genesis block to the current head block, executing all the transactions contained within. As the node crunches through the transactions, all past historical state is stored on disk, and can be queried for each and every block.</p> + <p>Initial processing required to execute all transactions may require non-negligible time and disk capacity required to store all past state may be non-insignificant. High end machines with SSD storage, modern CPUs and 8GB+ RAM are recommended.</p> + <br/> + <p>To run an archive node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: + <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> + <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=1024 --syncmode=full{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFullFlat}}</pre> + </p> + <br/> + <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> + </div> + </div> + </div> + <div class="col-md-6"> + <div class="x_panel"> + <div class="x_title"> + <h2><i class="fa fa-laptop" aria-hidden="true"></i> Full node <small>Retains recent data only</small></h2> + <div class="clearfix"></div> + </div> + <div class="x_content"> + <p>A full node synchronizes the blockchain by downloading the full chain from the genesis block to the current head block, but does not execute the transactions. Instead, it downloads all the transactions receipts along with the entire recent state. As the node downloads the recent state directly, historical data can only be queried from that block onward.</p> + <p>Initial processing required to synchronize is more bandwidth intensive, but is light on the CPU and has significantly reduced disk requirements. Mid range machines with HDD storage, decent CPUs and 4GB+ RAM should be enough.</p> + <br/> + <p>To run a full node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: + <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> + <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=512{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFullFlat}}</pre> + </p> + <br/> + <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> + </div> + </div> + </div> + </div> + <div class="clearfix"></div> + <div class="row"> + <div class="col-md-6"> + <div class="x_panel"> + <div class="x_title"> + <h2><i class="fa fa-mobile" aria-hidden="true"></i> Light node <small>Retrieves data on demand</small></h2> + <div class="clearfix"></div> + </div> + <div class="x_content"> + <p>A light node synchronizes the blockchain by downloading and verifying only the chain of headers from the genesis block to the current head, without executing any transactions or retrieving any associated state. As no state is available locally, any interaction with the blockchain relies on on-demand data retrievals from remote nodes.</p> + <p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Low end machines with arbitrary storage, weak CPUs and 512MB+ RAM should cope well.</p> + <br/> + <p>To run a light node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: + <pre>geth --datadir=$HOME/.{{.Network}} --light init {{.GethGenesis}}</pre> + <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre> + </p> + <br/> + <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> + </div> + </div> + </div> + <div class="col-md-6"> + <div class="x_panel"> + <div class="x_title"> + <h2><i class="fa fa-microchip" aria-hidden="true"></i> Embedded node <small>Conserves memory vs. speed</small></h2> + <div class="clearfix"></div> + </div> + <div class="x_content"> + <p>An embedded node is a variation of the light node with configuration parameters tuned towards low memory footprint. As such, it may sacrifice processing and disk IO performance to conserve memory. It should be considered an <strong>experimental</strong> direction for now without hard guarantees or bounds on the resources used.</p> + <p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Embedded machines with arbitrary storage, low power CPUs and 128MB+ RAM may work.</p> + <br/> + <p>To run an embedded node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: + <pre>geth --datadir=$HOME/.{{.Network}} --light init {{.GethGenesis}}</pre> + <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=32 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre> + </p> + <br/> + <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> + </div> + </div> + </div> + </div> + </div> + <div id="connect-go-ethereum-mist" hidden style="padding: 16px;"> + <div class="page-title"> + <div class="title_left"> + <h3>Connect Yourself – Go Ethereum: Wallet & Mist</h3> + </div> + </div> + <div class="clearfix"></div> + <div class="row"> + <div class="col-md-6"> + <div class="x_panel"> + <div class="x_title"> + <h2><i class="fa fa-credit-card" aria-hidden="true"></i> Desktop wallet <small>Interacts with accounts and contracts</small></h2> + <div class="clearfix"></div> + </div> + <div class="x_content"> + <p>The Ethereum Wallet is an <a href="https://electron.atom.io/" target="about:blank">Electron</a> based desktop application to manage your Ethereum accounts and funds. Beside the usual account life-cycle operations you would expect to perform, the wallet also provides a means to send transactions from your accounts and to interact with smart contracts deployed on the network.</p> + <p>Under the hood the wallet is backed by a go-ethereum full node, meaning that a mid range machine is assumed. Similarly, synchronization is based on <strong>fast-sync</strong>, which will download all blockchain data from the network and make it available to the wallet. Light nodes cannot currently fully back the wallet, but it's a target actively pursued.</p> + <br/> + <p>To connect with the Ethereum Wallet, you'll need to initialize your private network first via Geth as the wallet does not currently support calling Geth directly. To initialize your local chain, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and run: + <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> + </p> + <p>With your local chain initialized, you can start the Ethereum Wallet: + <pre>ethereumwallet --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFullFlat}}</pre> + <p> + <br/> + <p>You can download the Ethereum Wallet from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p> + </div> + </div> + </div> + <div class="col-md-6"> + <div class="x_panel"> + <div class="x_title"> + <h2><i class="fa fa-picture-o" aria-hidden="true"></i> Mist browser <small>Interacts with third party DApps</small></h2> + <div class="clearfix"></div> + </div> + <div class="x_content"> + <p>The Mist browser is an <a href="https://electron.atom.io/" target="about:blank">Electron</a> based desktop application to load and interact with Ethereum enabled third party web DApps. Beside all the functionality provided by the Ethereum Wallet, Mist is an extended web-browser where loaded pages have access to the Ethereum network via a web3.js provider, and may also interact with users' own accounts (given proper authorization and confirmation of course).</p> + <p>Under the hood the browser is backed by a go-ethereum full node, meaning that a mid range machine is assumed. Similarly, synchronization is based on <strong>fast-sync</strong>, which will download all blockchain data from the network and make it available to the wallet. Light nodes cannot currently fully back the wallet, but it's a target actively pursued.</p> + <br/> + <p>To connect with the Mist browser, you'll need to initialize your private network first via Geth as Mist does not currently support calling Geth directly. To initialize your local chain, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and run: + <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> + </p> + <p>With your local chain initialized, you can start Mist: + <pre>mist --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFullFlat}}</pre> + <p> + <br/> + <p>You can download the Mist browser from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p> + </div> + </div> + </div> + </div> + </div> + <div id="connect-go-ethereum-mobile" hidden style="padding: 16px;"> + <div class="page-title"> + <div class="title_left"> + <h3>Connect Yourself – Go Ethereum: Android & iOS</h3> + </div> + </div> + <div class="clearfix"></div> + <div class="row"> + <div class="col-md-6"> + <div class="x_panel"> + <div class="x_title"> + <h2><i class="fa fa-android" aria-hidden="true"></i> Android devices <small>Accesses Ethereum via Java</small></h2> + <div class="clearfix"></div> + </div> + <div class="x_content"> + <p>Starting with the 1.5 release of go-ethereum, we've transitioned away from shipping only full blown Ethereum clients and started focusing on releasing the code as reusable packages initially for Go projects, then later for Java based Android projects too. Mobile support is still evolving, hence is bound to change often and hard, but the Ethereum network can nonetheless be accessed from Android too.</p> + <p>Under the hood the Android library is backed by a go-ethereum light node, meaning that given a not-too-old Android device, you should be able to join the network without significant issues. Certain functionality is not yet available and rough edges are bound to appear here and there, please report issues if you find any.</p> + <br/> + <p>The stable Android archives are distributed via Maven Central, and the develop snapshots via the Sonatype repositories. Before proceeding, please ensure you have a recent version configured in your Android project. You can find details in <a href="https://github.com/ethereum/go-ethereum/wiki/Mobile:-Introduction#android-archive" target="about:blank">Mobile: Introduction – Android archive</a>. + <p>Before connecting to the Ethereum network, download the <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> genesis json file and either store it in your Android project as a resource file you can access, or save it as a string in a variable. You're going to need to to initialize your client.</p> + <p>Inside your Java code you can now import the geth archive and connect to Ethereum: + <pre>import org.ethereum.geth.*;</pre> +<pre> +Enodes bootnodes = new Enodes();{{range .BootnodesLight}} +bootnodes.append(new Enode("{{.}}"));{{end}} + +NodeConfig config = new NodeConfig(); +config.setBootstrapNodes(bootnodes); +config.setEthereumNetworkID({{.NetworkID}}); +config.setEthereumGenesis(genesis);{{if .Ethstats}} +config.setEthereumNetStats("{{.Ethstats}}");{{end}} + +Node node = new Node(getFilesDir() + "/.{{.Network}}", config); +node.start(); +</pre> + <p> + </div> + </div> + </div> + <div class="col-md-6"> + <div class="x_panel"> + <div class="x_title"> + <h2><i class="fa fa-apple" aria-hidden="true"></i> iOS devices <small>Accesses Ethereum via ObjC/Swift</small></h2> + <div class="clearfix"></div> + </div> + <div class="x_content"> + <p>Starting with the 1.5 release of go-ethereum, we've transitioned away from shipping only full blown Ethereum clients and started focusing on releasing the code as reusable packages initially for Go projects, then later for ObjC/Swift based iOS projects too. Mobile support is still evolving, hence is bound to change often and hard, but the Ethereum network can nonetheless be accessed from iOS too.</p> + <p>Under the hood the iOS library is backed by a go-ethereum light node, meaning that given a not-too-old Apple device, you should be able to join the network without significant issues. Certain functionality is not yet available and rough edges are bound to appear here and there, please report issues if you find any.</p> + <br/> + <p>Both stable and develop builds of the iOS framework are available via CocoaPods. Before proceeding, please ensure you have a recent version configured in your iOS project. You can find details in <a href="https://github.com/ethereum/go-ethereum/wiki/Mobile:-Introduction#ios-framework" target="about:blank">Mobile: Introduction – iOS framework</a>. + <p>Before connecting to the Ethereum network, download the <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> genesis json file and either store it in your iOS project as a resource file you can access, or save it as a string in a variable. You're going to need to to initialize your client.</p> + <p>Inside your Swift code you can now import the geth framework and connect to Ethereum (ObjC should be analogous): + <pre>import Geth</pre> +<pre> +var error: NSError? + +let bootnodes = GethNewEnodesEmpty(){{range .BootnodesLight}} +bootnodes?.append(GethNewEnode("{{.}}", &error)){{end}} + +let config = GethNewNodeConfig() +config?.setBootstrapNodes(bootnodes) +config?.setEthereumNetworkID({{.NetworkID}}) +config?.setEthereumGenesis(genesis){{if .Ethstats}} +config?.setEthereumNetStats("{{.Ethstats}}"){{end}} + +let datadir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] +let node = GethNewNode(datadir + "/.{{.Network}}", config, &error); +try! node?.start(); +</pre> + <p> + </div> + </div> + </div> + </div> + </div> + <div id="about" hidden> + <div class="row vertical-center"> + <div style="margin: 0 auto;"> + <div class="x_panel"> + <div class="x_title"> + <h3>Puppeth – Your Ethereum private network manager</h3> + <div class="clearfix"></div> + </div> + <div style="display: inline-block; vertical-align: bottom; width: 623px; margin-top: 16px;"> + <p>Puppeth is a tool to aid you in creating a new Ethereum network down to the genesis block, bootnodes, signers, ethstats server, crypto faucet, wallet browsers, block explorer, dashboard and more; without the hassle that it would normally entail to manually configure all these services one by one.</p> + <p>Puppeth uses ssh to dial in to remote servers, and builds its network components out of docker containers using docker-compose. The user is guided through the process via a command line wizard that does the heavy lifting and topology configuration automatically behind the scenes.</p> + <br/> + <p>Puppeth is distributed as part of the <a href="https://geth.ethereum.org/downloads/" target="about:blank">Geth & Tools</a> bundles, but can also be installed separately via:<pre>go get github.com/ethereum/go-ethereum/cmd/puppeth</pre></p> + <br/> + <p><em>Copyright 2017. The go-ethereum Authors.</em></p> + </div> + <div style="display: inline-block; vertical-align: bottom; width: 217px;"> + <img src="puppeth.png" style="height: 256px; margin: 16px 16px 16px 16px"></img> + </div> + </div> + </div> + </div> + </div> + <div id="frame-wrapper" hidden style="position: absolute; height: 100%;"> + <iframe id="frame" style="position: absolute; width: 1920px; height: 100%; border: none;" onload="if ($(this).attr('src') != '') { resize(); $('#frame-wrapper').fadeIn(300); }"></iframe> + </div> + </div> + </div> + </div> + + <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/js/custom.min.js"></script> + <script> + var load = function(url) { + $("#connect-go-ethereum-geth").fadeOut(300) + $("#connect-go-ethereum-mist").fadeOut(300) + $("#connect-go-ethereum-mobile").fadeOut(300) + $("#about").fadeOut(300) + $("#frame-wrapper").fadeOut(300); + + setTimeout(function() { + if (url.substring(0, 1) == "#") { + $('.body').css({overflowY: 'auto'}); + $("#frame").attr("src", ""); + $(url).fadeIn(300); + } else { + $('.body').css({overflowY: 'hidden'}); + $("#frame").attr("src", url); + } + }, 300); + } + var resize = function() { + var sidebar = $($(".navbar")[0]).width(); + var content = 1920; + var limit = document.body.clientWidth - sidebar; + var scale = limit / content; + + console.log(document.body.clientHeight); + + $("#frame-wrapper").width(content / scale); + $("#frame-wrapper").height(document.body.clientHeight / scale); + $("#frame-wrapper").css({ + transform: 'scale(' + (scale) + ')', + transformOrigin: "0 0" + }); + }; + $(window).resize(resize); + + var item = $(".side-menu").children()[0]; + $(item).children()[0].click(); + $(item).addClass("active"); + </script> + </body> +</html> +` + +// dashboardMascot is the png dump of the mascot to display on the dashboard about page. +var dashboardMascot = []byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01s\x00\x00\x02\x00\b\x06\x00\x00\x00p\xe4\x8c`\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\tpHYs\x00\x00\v\x13\x00\x00\v\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\atIME\a\xe1\x03\x1d\x0e0&\xf3\xca\t\x11\x00\x00\x00\x1diTXtComment\x00\x00\x00\x00\x00Created with GIMPd.e\a\x00\x00 \x00IDATx\xda\xec\xbdw|\x15U\xfe\xff\xff<3s[\x12BB\x12H!\x90\u0411&H\a\x01i\"V\xb0!b\xc1^>\xbb\xee\xae\xdd\xd5u\xed}-k/X\u058e\xa8\xa0\u049bH\x87\bH\xef\x04BI\x81\xf4[\xa6\x9c\xf3\xfbcnB`\xdd\xef\xcf\xdd\xd5\xddU\xe7\xf9x\\\u023d\u027dw\xee\u0339\xafy\xcd\xfb\xbc\xcf\xfb\r\x1e\x1e\xff\";wn\x17\xb7\xdf~\xabQw_)\xa5+\xa5\x1a)\xa5\u0494R\xc9J\xa9D\xa5T\xf0\xb8\xa7\xe9\x1f|\xf0\x9eQYyXx{\xd0\xc3\xe3\xc7\xc3\xfbBy\xfcK\xbc\xf3\xce[\xe2\xd2K/Wq\x11o>y\xf2G'\xaf[\xb7\xae\xff\u05ad\xdb2\xa5\x94\xa9\xd1h\xb4:---\u06b9s\xe7HJJ\xf2\xb2f\xcd27\x9e}\xf6\x98\"!\xc4\u07ba\xd7x\xf6\xd9g\xb4\xcb.\xbbD\xa6\xa4\xa4\xb1d\xc97\f\x18p\xb2\xb7c=<<1\xf7\xf8O\xf0\x97\xbf<EII\x89x\xf4\xd1\xc7\x14\xc0\x8c\xe9_\x0e\xffv\u035a\xa7\u05ec]\u05f9`\xf5jJK\xcb0M\x13M\xd3\b\x85B4i\x92J\xabV\xf9\xe4\xe5\u54d9\x99Y\x90\x9a\x9a2\xa5_\xbf\xfeS\xfb\xf5\ubfe9\xee5o\xba\xe9V\x9e}\xf6\t\x94R\b\xe1\rI\x0f\x0f\x0f\x8f\x9f\x9c\xf7\xde{\xb7\x81;\u007fs\xe4\xc4+.\xafi\u04e6\x8d\x02\xfe\xe1M\xd75\x95\x9c\x9c\xacZ\xb7n\xadF\x8d\x1a\xa5\u018d\xbbp\xd7=\xf7\xfc\xf1\xf5\xaf\x17.\x18q\xfc\xeb\u007f\xfa\xe9'\xdeN\xf6\xf0\xf0\xf0\xf8\xa9\xc9i\x9e\v\xc0\x97_N\x1bq\u0265\x97\x1c\xceh\u06b4^\xb4\x85\xa6)\xc3\xe7S>\xbf_\x89\xef\x15u]\x86B!\x99\x93\x93\xa3\xfa\xf6\xed\xab.\xb8\xe0\x82\x8a\a\x1e\xb8\xef\x83}\xfbv\xf7k\xf8\x1e\u007f\xf8\xc3\x1f\xc4\u0739\xb3\xbc\x9d\xed\xe1\xe1\xe1\xf1S\xb0r\xe5r\r@)\xd5\xee\xf6\xdbo\u06d2\u07fa\xb5\x02$\xa04]W\x86\xe1S\t\u024dUjV\xaeJ\xcdn\xa9\x12S\u04d5\x10\xe2\x18A\x17B(\xbf\xdfo'5jde\xe74W\xfd\xfa\xf5WW^yE\xe5k\xaf\xbf\xfa\xa0R*\xb1\xee\xbdz\xf7\xee#\xdex\xe35o\xa7{x\xfc@\fo\x17x\xfc\x10\x94R\x9cz\xeaH\x00\xfe\xf6\xb7\xb7\xaf\u0634yK\xfb\xdd;w\u0680\xa1\xe9:\x86\xcfO\x93\xdc\xd6t\x18r\x06\x19m\xbb\xa0\xe9\x06f\xd5\x11\xcavmd\xff\xa65\x1c\u06be\x91pU\x05J)L\xd3\xd4\x1d)\xb1\x1d\u01e9\xae\xae\xe1\u0421\x83\u027bw\xef\xfe\xe3\x96\xcd[\u03999s\xfam\xa3F\x8d\x9e\xber\xe5\n\xb5r\xe5\n>\xfa\xe8\x03q\xe1\x85\x17)\xef\bxxxx\xfc\b\u0318\xf1U\x9d+O\xbe\u66ab\xe7\xb6\xcc\xcbW\x80\x85\x10J7|\xaay\x97^\ua497\xa7\xab{\xd7Ku\xcfw\x8e\xbao\x83\xa3\x9e\xd8\"\xd53[\x1d\xf5\xe0\xd2\xfd\xea\xca\x17>Q\xfd/\xbcZ\xa5\xe5\xe6\xff]X&!1Q\xa6\xa5\xa5\xa9\xf6\xed\u06ebs\xce9[=\xfa\xe8#\x1f\u06b6\x99Y\xf7\u0793&\xbd\xe1\x99\x0e\x0f\x0f\u03d9{\xfc\x18l\u0630A\x03\xe4\xf4\xe9\xd3z\x95\x1d>\xd2\xfdPq1\x80.4\x8d`R2\x9dF\x9eG\xab\xc1\xa7\x11\x8dJ|\xd2B\xf3\t4\f4M#%3\x9b\x93\xce<\x97\x13O=\x97\xc2u+Y\xf1\xc9$VM{\x8fhm\r\xb6\x94H\xc7\x11JJu\xe8\xd0!QYYIY\xd9\xe1\v\v\v\v{\xbf\xfa\xea\xcb\u007f\xbc\xfa\xeak?\x12B\xd8c\u019c\xa3=\xf0\xc0\xfd\xaas\u7b9eK\xf7\xf0\xf0\xc4\xdc\xe3\xdf\b\xb3\u803dq\u00e6v\xa6i6\x89E\xc2\x12\xd0p\x1c2\xf2\xdb\u04a6\xcf`t\xc0pb\x04|\x02\x03\xb0-\vi\x83\xa6\t\x10\x02C\xf7\u0476ooZu\xefM\x87\x81#\x98?\xe9iv\x16,AJI$\x12\x11J)4Mc\u02d6-\x1c<x0\xff\u0421C\xefWTT\x9c\xa7\x94\xfa\x83\x10\xa2\xf0\xb3\xcf>\xe7\xc9'\x9f\x10II\x89\xea\xba\xebn\xf0\x0e\x8a\x87G\x03to\x17x\xfc\x10\xe6\u0319\xeb(\xa5\xf4\x85\v\x17\\\xb5x\xf1\u0493JJJ\x00\x84\xa6\xe9\xe4\x9f4\x90^\xe7\\\x8a\xdf\x1f\xc4'\x1c4\x01 @\x80B\xe1(\x85t\x14\xb6\xed`\xc5\x1c\x84\xd0\xc9\xed|\x02\xed\xfb\x8d\xc4\x17\brp\xebz,3\x86m\xdbH)\xd14\r\u02f2\u063f\u007f?\xd5\xd5\xd5\x1d\xb7n\xdd:\xfc\xfd\xf7\u07dd\xf3\xf4\xd3\xcf\x1c\x99={\x0eYYYb\xfc\xf8q\u031e=\xc7;0\x1e\x1eq4o\x17x\xfc\x13\xe4X\x96\u0569\xa2\xa2<\xae\xd6`\x04\x83\xa4d\u5498\x92\x8a\x81D\x17u+\xd1\x14(\x85T \x158\xb87KB,\x16\xa5\xb6\"L\x93\x9c\xe6\x9cu\xcb\xc3\\\xf9\u0707\xb4\xee\xd1\x1f\x00\xd34\xa9\xad\xad\xc54M\xa4t\xd4\u06b5\xeb\x983gn\xe7G\x1ey\xec\u04d2\x92\x03\xb9\x00\xaf\xbd\xf6\xba*--\xf5V\x17yxxb\xee\xf1\xafP[Se\x16\x15\xed\x8fX\x8e\x13Wk\b$$\xd18\xb39\x9a\x06\x02\x85&@\x8b\vz\xc3\u015c\ueb27B\x01\xb6\u0490B'\\S\x8be\xd9t\x191\x8a\u02df\xf9\x80\xbec/\x03\xc0\xb6mjjj\x88FcB\b\xa1v\xed\xda\u0152%K\xbb\xfc\xe1\x0f\xb7~\xban\u0777-\x00\x1e{\xec\t5c\u0197\x9e\xa0{xxb\xee\xf1\xcfR\xb4\xbfH\x84\xc3a\u0371\xed\xfa\xc7|\x81 \xa1\u4538\x11W8J \x95\xa83\ueba0\xbb&\x1d\xea\\\xbar\x1d\xba\x12:\u04b1\xa9\xad\xb6i\u05a6\x05\xe3\x1fy\x83\xb3n~\x88P\xa3d\xa4\x94\xd4\u0506\tG\"BJ\xa9\n\v\vY\xb7n]\xcfG\x1ey\xec\x8b\x193\xbe\xec\fp\xdaig\xa8\r\x1b\xbe\xf3B\x85\x1e\x1e\x9e\x98{\xfc3\xa4\xa4\xa4\xa0i\x9aRR\x1e}P\b\x94Tq\xe7-\u0730J\u0703\u02fa$\u0138So\x98\x86\"\x15\xd8R\xa0\x14(\xe9P]a\xe2O\xd09\xf5\x86\xbb\xb8\xe0\xde\x17H\xcdj\x8e\x92\x0e\xe1p\x98H4*\x14\xa8\x83\a\x0f\xb1a\u00c6\xae\xef\xbf\xff\xc1\x97\v\x16\xcc;\x1f\xa0s\xe7\xae\xce\u0319\u04fdq\xec\u1279\xb7\v<~(\u035ae\xeb)\xa9\xa9\xbe:\xd3\r`\x9b&\xe1\x8a#q]\x17\xf5\x95\xdb\xd4q\xff\x1f\r\xb7\xa8\xfa\u01dc\xb8\x93\a\xd7\xd5\xd7VE\xb1\xa5\xa2\xf7y\x13\x18\xf7\xd0\xeb4ks\x02(E4\x12!\x12\x8d\n\u02d1\xeaPq\t\u02d7\xafh\xf9\xe1\a\x1f~<s\xe6\xf4\xdb\x00F\x8d\x1a-\xdfy\xe7m\xf1\xd9gS\xbc\x83\xe4\u1279\x87\xc7\x0f\xa0*))\xb1\xd8\xe7\xf3\xd7\xeb\xb4\x15\x8dPY\xb2\x1f%\xeb*\x1e\xaa\xb88\xbb\xee[\xd5\u074e\x13\xf6\xba\xfb\x96t\x1d\xbc\x16\u007f\x8e\x15\x8b\x11\x8b\xdat\x1av*\xe3\x1ez\x9d\xdc\u03bd\x000\xa3Qj\xc3a\x11\xb5lJ\x0f\x973c\xe6,\xde\u007f\xff\x83\u01fe\xf8b\xeam\x00\x97^z\x99Z\xbf~\x83\x98<\xf9#\xef(yxb\xee\xe1\xf1\x8f0\f\x9f.\x84\xa8\xb2L\xab --\r\xdc:+8\xb6EU\xc9Aj+\xcaA\xd3P\xc7LI\n\xb7p\xcbqB~\u053d\v$\x02[\xb9~]\x13\x02M\b\xa4m\x13\xad1i\u0777\x1f\x17>\xfc:\xad{\x0f\x06\xc0\x8eE\x89\x86\u00d8\xb6Cyu\r\xf3\xe7/\xe0\x93O\xa6<\xb6h\xd1\u009b\x00\xfe\xf4\xa7{\u055a5k\u0164I\xaf{\a\xcc\xc3\x13s\x0f\x8f\xef\xe3\xfe\a\xfe\xac\x03\xb4i\u04fa\xbcY\xb3f\xf5\x9a,\x1d\x87\xaa\u0483T\x1c\xda\x17\x9f\xect\xeb%\xba\x99,\xea8\xf9v\u007f\xaf\xe2.\xbc\xee/l)p\x94h\x10W\x17\xeeB\xa2\xea(\xb9\u077ar\xc1C\xaf\xd3a\xd0i\xae\xa0\x9b1\xccH\x18\u01d1T\xd4\u052a\xb9\xf3\x17\xf0\u059bo=\xb3`\xfe\u071b\x00\x1e~\xf8\x11\x15\x8b\u017cq\xed\u1279\x87\xc7?\xc0\x01h\u07fe\xfd\x16M\xd3\x0e\x01B)\xa5P\x8a\xea\xb2b\x8e\xec\u0749\xe1sE\xbc\xae\xc1\x84\x10 \x8e\x9f\xfa\x14\xaa~R\xb4>$\x03\xd8Ra\xab\x86a\x18\xe1\xc6\u02ebbd\xb5o\xc3y\xf7\xbfD\x87\x93G\xb9\x82n\x99\u0122a\x1c\xa9DyU\x8d\x9c5g\x1e\x93&\xbd\xf9\xccg\x9f~\xf2;\x80\ubbffQ~\xfa\xe9'\xde\xd8\xf6\xf0\xc4\xdc\xc3\xe3x\xfa\xf4\xee\xad\x00\x86\x0e\x1d\xbe==#cKBR\x12\xae\xc1\x96\xd4\x1c.\xe6\xd0\xf6\x8d\xf1\xa5\xfb\xc7\x0f\xa9\xe3\x03,\xc2\x15\xf4\x06\x8f\v\x14J\x81\x1d\x8f\xb3\x83B\x13\n\x81\xe6\nz\xb5Ef\xbb\x96\x9c\xff\xd0kt\x19y\xae{f1M\xccH\x18\xe98Zyu\xad\x9c\xbb\xe0k>\xfc\u88e7\xdf{\xf7\x9d\xdb\x01\u018e=O\xae^\xbd\xd2K[\xf4\xf0\xc4\xdc\u00e3!C\x87\x0e\x97\x80\x10B\x1c\xc8i\xde|k\x8b\xdc\\\x00[I\x89\x15\rS\xbag;U%\xc5\xe8\x86\xcfm\xff\u01b1\xee\\\x1c\u04e1P\x1c\xa3\xf3*\x1e\x9d\xc15\xfa\rD\x1f4\xcdM]\xac=b\x91\u05a29c\xef}\x81\ue9cfs\x05\xdd2\xb1\xcc(J)\xad\xaa6\xa2\xbeY\xb2\x8c\xaf\xa6O\u007ft\xea\xd4Oo\a\xe8\u0673\xb73c\xc6W\xde\xc2\"\x0fO\xcc=<\xea\xe5W\b\x9ey\xe6\x19\r\xa0u~\xfe\xdaF\x8d\x1b\x03\xf8\xa5m#\x1d\x87\xe2]\x9b9\xb4s\x13\xbe@\xbd<\xd7GR\xc41.\xbdAz\v\r&G\xd5\u047fQ\xf5r\xee\xfe\xa4\t\x01\xca!V\x15%-\xa7\x19c\xee~\x8e\xeeg\\T\xef\u042dh\x18)\xa5\xa8\xac\x8d\xb0h\xf1R\xbe\xf8\xe2\xabG\xe7\u039d5\x01\xe0\xb4\xd3NW\x0f<p\x9fX\xbdz\xa5w\x10=<1\xf7\xf0\x00\xc8\xcfo)\x01.\xba\xf0\xbc\x95\xe9\xe9\x19\xbb}\xfe\x80+\xbfJq\xa4h\x0fE\x1b\v\xb0M\xd0u\xbd\x81\xcbv\xe5\xf9\xe8\x9a\xd0:\xbb\x1e\x97\xeacD\xfd\xa8\x84\xcb\xe3\x025Z<\x16\x1f\xae\x8a\x92\x9c\x95\xc1\xd9w=\u0349\xa7]\x00\xb8\x93\xa2V4\x8cr$\xe5\u0575\xcc_\xf05\x1f~\xf8\xf1\x13\u02d6~\xd3\x15\xe0\x9e{\xeeU_\u007f\xbdH\x1c<\xb8\xdf;\x88\x1e\x9e\x98{x\x9cr\xca)\n \xb5I\u01b7\x1d\u06b7\u06d9\x95\x95\x05\xa0\xa4ccFj)\\\xb3\x8c#\xfbv\x11\b\xf9Q\xaa.\xa5\xa5\ue98e\xae5\xaa_\x19ZW\x95+.\xe0\xf1\xa5\xfeu\x81\x96\xfa\xfctE\xbcD\x80[N7Z\x15%5\xbb\x19g\xde\xf54\x9d\x86\x9fsT\xd0\xcd(JA\u0251\n\x96._\x99\xf9\xde\xfb\x1f\xbe\xab\x94\xca\x03\xb8\xf9\xe6[\u051bo\xbe\xe5\x1dD\x0fO\xcc=<\x92\x93S\xea\u007f\xee|B\x87\u03dbff\x02\b\xe56\x98`\xef\x86\x02\xf6\xac]\x1e\x17]\xcd\xf5\xe4B\x1d\xe3\xb0\x05G\x8bn\x1d\x1bEw\x05\xdd]\x15\x1a\x1f\x9c\xc2\x1d\xa0\r\x9f\x03\nM\x83hu\x94\xf4\xdcl\u03be\xebiZ\xf5r\xf3\u042dh\x04;\x16E*\xa5\xf6\xec\xdb\xcf\xeao\xd7ty\xe4\xe1\x87\xea\x15\u0732L/~\xee\xf1\x8b\u015b\xed\xf7\xf8\x97\x98:u\u06bey\xf3\xe7\x8f(\xda\u007f 3\x16\x8d\x80t\xb0bQ\x1c\u01e1y\xe7\xde$\xa7\xa5#m\xbbA\xce9GeY\x88\xfa\xf4E\x94:\xb6\xbcb\xbd+\x17\xf5\xa1\x97\xba\x12\x00uN]w\xb3\x16\xb1mh\u04bc\t\xcd\u069e\xc4\xfe\xf5\xab\xa9*9\x80\xe3\xd8h\x9a&\x10B\x95\x1d>,L3\x9a\xf7\u007f7\u07980{\xf6\xec\xb9\v\x17~\r\xc0k\xaf\xbd\xca\x17_|\xe1\x1dD\x0f\u03d9{\xfcz\xb9\xe1\x86\xeb]\xc1\x15\xe2\u0420\x81\x03\x16d\xba\xee\xdc\xcd\x15\xb7,\xf6|\xbb\x94\xdd\x05\x8bQJa\xf8|GE\xbb>\x98\u00b1\xf1\xf3\xfag\x1f]`TW\xac\xcbQ\x02[\x1d\x9f\xc4\xe8.0B\b\x94c\x13\xaeth\u0465\vg\xdc\xf9\x14i-\u06c0RX\xb1\bJ\xda\xc2r\xa4Z\xb7~\x13\x05\x05\x05\xb7\u035e=}\f\xc0\x94)\x93\xf5\xab\xaf\xbe\xc6;\x90\x1e\x9e\x98{\xfc\xba\xb9\xf6\u06ab\xeb\u007f>}\xf4i\x9ft<\xa1\x83\n%$\xba\x8a\xac$\xe1\x8a26\xcd\xff\x9c\u0292\x03\xf8\x03:\xba\x00\xedh\xbb\x8a\x06\x01\x95\x06%\x15\x1b8\xf2\xba`\x8a#AJ\x15\xaf\xaa\x18\xbf\xc5\v\xbe\xd4M\x90\n\x01\xca6\x89\xd5Z\xb4\xedw2\xa7\xfd\xfea\x12S\u04d0\x8e\x83m\xc6\x10(Q\x13\x8eX\x9b6oe\xc6\xf4YW\x01\x9c{\xee\xf9\xce\u0295\u02fdq\xef\u1279\u01ef\x9bn\xddz0y\xf2\xc7\x00\xe4\xe7\xb7^\u04a3[\xb79\xd99\xd9\xf5Z,m\x8b\u00b5+(\\\xbb\x1c!\xc00\f4\xa1\x8e6\xach \xe0\xa2\xc1?nhE\"m\a\u01f4pL\x13\u01f6\xdd>\xa2\xf1a\xea\x98Ql3\x86m\xd9HG\xba\xb1u!P\x8e\x85\x15u8q\xf4\xf9\f\x9ax\v\bp,\v\xdb4\xd1\f\xdd\xd8Y\xb8\x97\xad\xdbw\x8c\xfe\xf8\xe3\x0fo\x04\xe8\u077b\xaf\xfa\xee\xbb5\xde\xc1\xf4\xf0\xc4\xdc\xe3\xd7\xcd\xf9\xe7_P\xffs\xcb\x16-\x1e\xec\u043e\x1dqw\x0e@e\xf1~\xd6N\xff\x98\u02b2rt\xbf\x8e@\xb8\x0e\xbd~y\u007f\u0709\xd7?\xa6PR\"-\xcb\x15k3\x86c\x9b\x14\x16,a\xf6Sw1\xf7\xaf\xf7rp\xdb\x06\x84\xeesE:\x1a\x8d\xf7\fu\x90\n\x84\xa6\xe3X&\n\xe87\xfez\xba\x8d\x1eW/\xfe\x8ei\nG*\xb9u\xfbN\xbe-\xf8\xf67J\xa9\x96\x80\x9a9s\xb67\x19\xea\xf1\x8b\u009b\x00\xf5\xf8\x97\x989s\x86x\xf7\xdd\xf7\xf8\xec\xb3\xcf\x0f.[\xb6\xb4WQQQ\x9b\xca\xca\xca\xfa\xdf\xd7\x1c)\xa1i\xab\x0e4?\xa1\x13\x8eS\x97\x9a(\xe2\xf9\u3abe\xad\x9c\x06(i#-\vi\xdbH\xdbB)\x89c[\xac\xf9\xfcmV|\xf0\x02\xfb\xd6-\xa7t\xf762\xdbu%9#\x13;\x16E9\x0e2\xde$C\x17\x02M\u05f0-\x8bPr\x12\xa9-:\xb0k\xd5\"j\x8f\x94 \x1d\x1b\xc3\xf0\x8bp4\x8a\xa6\x91n\x9b\x91\xa2i\u04fe\\6g\xce\\f\u039c\u03bb\xef\xbe\xe7\x1dL\x0f\u03d9{\xfcz\x195\xea4w\xe1\xbd\x10V\x97\u039d\xa6\xb6i\u075a@0T\xff\xfb\xda#\xa5\xac\xfd\xea#*\x8b\xcb\xf0\x05\xf4z'\xae\x8b\xa3\v\x88\x04\xa0\xa4\x04\xdbF\xd9\x16\u04b6\x90\x8e\x83\xb4,t\xdd 5;\x8fPr*\x00\xbbW.\xe4\x9bIOR]V\x8c\xee\xf3!-\x13\u01ccaG\xa3\u0626+\xee\x9a\xd00\xc3&M;t\xa6\xcfe7\xa3\xf9\xfc()1c\x11\xa4\x92l\u0676\x83\xd5\x05k\xae\t\xd7V\xe7\x02L\x9c8\xd1s\xe7\x1e\x9e3\xf7\xf0\xa8\xe3\x8b/\xbe(.(X5\xbch\xff\xfef\xb5\xe1\bR:\x00T\x1c\xdaKJ\xb3\x1cZ\x9e\xd8\vi+\x94\x92\xf5\x8b\x81\xa4\x02\xa9\x14\u02b6Q\xb6\x8dt,\x1c\xc7A92\xbezT\x91\x9e\xd7\x16\xcd0(\u0779\x19\xc74)?\xb0\a#\x98@\xf3N'!m\x1b%%B\xc9x-\x18\x88w\x95F\t\x1f)\xf9\x9d)\u07fb\x9d\xd2\xed\xebQn\x03j,[\xe2\xd8vZ\xf9\x91\xb2\xb2\xf9\xf3\x17|SSS\x8bR\x92\xfb\xee\xbb\xcf;\x88\x1e\x9e3\xf7\xf8\xf5\xb2c\xc7\u05b89\xd7\xf6\xf5\xec\xd9\xf3\xf5.\x9d:\x11\b\x06\xd0\r\x03\x003\\\u02ea\xcf\xdf\xe1\u040e\x1d\x18A\xb7\x00\x97T\x10\xb3%Q\xcb\xc1q$B)7\\\"\x15\x9a\xaa\x13g\x89c\x99\x18\xfe }\xc6]\xcf\xc0\x897\u04e8i6V4\xc2\xe6yS)Z\xbf\x1a\xdd\x1f@96R\xd6M\x9a\x9a(+\x86\x94\x92X$\x8a\x11\xf4\xd1k\xfcoIHM\a\xeaV\x88\xc6\xd8[T\xc4\xe6-[\xafUJ\xba+\x9e\x84\xf7\x15\xf0\xf0\x9c\xb9\u01ef\x9c\xe7\x9e{\x9e/\xbf\x9cf\xbc\xff\xfe\a\xf2\xb3\xcf>\xdf\xf5\xdd\xfa\xef\x86\x1e<x(\xbb\xaa\xaa\xcaQRjJ)*\x0f\x15\xe1\x0f%\u043a\xcf0\x14\x02\u06f6\xb1\xe2\xe9\x85~\rt%\x91\xb6\x8d\xe3\xd8 \x9dz\xc1\x17\n\xa4c\xa3\x19\x06\xd9't\xc7\x17J\xa0d\xc7F\xaa\x0e\x15\xa1\x80\xecN=1\x82!pl4\r\x10\xae\x9b\a\x81\xe9\x80\x14\x06\x8d\x9b\xe6Pyp/\a7\xaer\xdd~<\x97\xc6\xd0\xf5\u0186&J\xa7N\x9d\xb6\x04`\xf1\xe2EL\x9a\xf4\xa6w@=<g\xee\xf1\xeb\xe5\xf4\xd3\u03f4;th\xaf\v!JO<\xb1\xdbcy-[\u0621PH7|>Y\xe7zWN\x99\u0136of\x12Lr\x1d\xbbO\x17\x04}\x1aZ\xbc\u035c\xc2\x15b'\xde4T(U\xbfX\u050aF\x90\xb6M\x97Q\xe7\xd3\xed\x8c\xf1\x18\xc1\x10;\x97\xcfg\xc7\u04b9h\x9a\x8e\x00\xa4\x94\u060eD:\xd2\x15w\xe5\x80\x19!\x10\xd4\xe8|\xda8B)\xae;\x97\xb6I,\x16\xa5\xb8\xb8\x84\u056b\v\xaePJ5\x05\x188p\x90w =<1\xf7\xf8al\u07bc\xe1\u07dal[\xb3\xa6\xe0\u007f\xb2\xea\x9f\x10\x82\u0673g\xba\xd5\x14/\xbaxr\xabV\xf9S\xf3Z\xb6@\xd34G\xb8\xa5\x0e\xa99\\\u02827\x9e\xe4\xf0\xde\x03$$'\xa0\xa1\u0705D\x02\x948Zd\xcb\xed\x17\x1a_\xbc_W\x17W)\xcch\x18!4:\x8f<\x8f\xfc\x9e\x83\x88V\x1da\xc3\xec)\x1c.\u070e\x11\f\xe1H\x85\x94\xf1\xd5E\xd2A96\xb6\xe3\x10\x8bJ2\xdau\xa5\xc3\u0433\u074dU\n\u01f69|\xa4\x9cM[\xb6\xb4\xff\xec\xd3O\xeas,\xa7O\xff\xd2\x1b\xa4\x1e\x9e\x98{\x1c/\xdc\x1b\xffN\xb8;v\xec\xac\xfe\x9d\xd7\xec\xde\xfd$\x95\x95\x95\xf3w\x8f\xef\u06f7\xe7\xbf\xfaY\x8b\x8a\xf6\u04a2E\xbe\x9a>\xfdK\r\u0b33\u039a\x94\x97\x97\x17m\x94\x94\xe4\xf3\x19>U\x97W\xbec\xf9|\xbe\x9e\xf4$2f\x13LH<\xdaRN\xd3\x11\xba\x01B\xe0H\x85-\xdd0\x8b\x8aOl\xd6aE#4JoF\xd7\xd1\xe3Hm\u078aC\u06fec\u04c2/\x88\x86k1|~\x90\x12\u02d1\x98\x96\x83m\xdb\b\xe9\xe0\xc4\xc2\x04\x1b5\xa6\u07503\xf1\x05\x13\\w\xee\xd8D\xa2QJJ\xcbX\xb0p\u163a\xd7\x1f=\xfa\fo\xe0z\xfc\xac\xf1R\xb3\xfe\r\x94R\u031f?W[\xbbv\x9d\xb6k\xd7N\xfd\x85\x17^\x8a\x1d\xf7\xfb\x04)\xed\xc0\u0739s\xd2g\u03de\xa3L\xd3N\xea\u07ff\uf165\xa5\xa5I\u06f7\uf215\x96\x96\x12\x0eG\xb0m7\x93C\xd7u\f\xc3 11\x91\xa6M\x9b\x92\x97\x97\xe7\xcf\xc8H\x0fo\u077au\xea\x91#\x87\xcbN9\xe5\x14\u0577o\u07f2\x9c\x9c\x16\xa6\x10\"|\xdc\xe6\x18\x8f=\xf6\xa8\xc8\xc9\xc9v.\xbe\xf8\x12)\xc4\u007f\xef\xd0>\xf8\xe0\xfd\x0f|\xf5\xd5\xf4\xbb7n\xdeJ$\x1cV\x96e\n\xe2\x19'g\xdd\xf9\x17\x06^z\x13\b\x81m\u0190\nl\xd3$V[K4\x1aA\xd96B\x88x\xfdrw\x88\xaa\xb8\xab\xd6t\x1dG\x18,\xfb\xf8u\xd6M~\x89\x84\x944\x86\xff\xdf}\xb4\xee=\x18+\x16\xc1Q\x02\x89@7\f\x94\xe1G\xe9>\xfc\t\x89\u012a\x8e0\xe3\xc1\xeb\xd9<\xf73\x00t\u007f@\xa56N\x16}\xfb\xf4\xae\xbe\xf7O\xf7\\\u052bw\u07ef\x00\xbe\xfbn\r]\xbbv\xf7\x06\xb6\x87'\xe6\xbf&\xbe\xfa\xea\v\xed\x0f\u007f\xb8\x85\xad[\xb7\xca\x06\xe2ml\u0630\xb6\xcd\xe4\u025f\xa4WUU\xb7\xf6\xfb\x03\xc3\xf7\xed+\xca\b\x04|\x03\x0e\x1e<\x94\xb4\u007f\xff\x01\xc2\xe10\xb1X\x94X\xcc\xc4i\xb0\xf0E)U_\x94J\xd34\fC\xc7\xe7\xf3\xe1\xf7\a\b\x85\x82dee\u046cY\xb3\x9ah4\xba8''\xa7\xaci\u04cc-J\xa9UC\x86\f\xd9;h\u0410\"!DM\xddv\xa4\xa5\xa5j/\xbe\xf8\"\x17\\0\xee\xbf\"\xeaJ\xa9\x8c\x1b\u007f\xfb\xdbY\u04e7\xcf\xe8^\\\\\x8c\x15\x8bb[\x16\x00\xa1F)\x8c\xfd\xd3_\xe9u\xee\x04\xa2\x11\a\xe9\xd8\u0636\x85Y[K,\x12\xc1\xb6,\x84R\b];Z\x90K\xc5\xc3/Rb\x1a\t\x1c\u06b7\x97o'=\xc8\xfe\xb5\x8bi?\xf8L\x86]\u007f7\xc1\xe4\x14b\xd1\x18a\a\xfc>\x03\u007f\xc0\x87\xa3\xfb\xd14\x8dPJ\n\x05\x1f\xbd\xca\xf4\a\xae\a%\xd1t\x03\u007f {\x9d\xd4C\x1b}\u06a8G\xee\xbc\xeb\x8fwy#\xda\xc3\x13\xf3_\t\xf3\xe6\xcdf\u0630\x91L\x9a\xf4\x12\x13'^W/\xbcJ\xa9\xd0\xec\xd93\xfb\u035a5+\xaf\xa4\xa4\xb4\xb7eY\xa7TTT\xb4\u06f3\xa7\x90\x92\x92\x12,\xcbB)E0\x18\xa4q\xe3\xc6$''\x93\x9c\u0708\x84\x84D\x12\x12B\x04\x02\x01t]G\xd7uw\"\u03f6\x89FcD\"a\xc2\xe1\b\xd5\xd5\xd5TUUQQQA4\x1a\x05\xc0\xe73h\u04a4\t\xf9\xf9y4i\x92V\u0628Q\xa3e\u035b7_\u06f6m\x9b\xe5\x13&\\\xbaR\b\x119\xea\x92\x1f\xc4q\x14\xfd\xfa\xf5d\xe4\xc8\xd3~\xd2}t\xf7\xddw\xf1\xe0\x83\x0f\x030k\xd1\xe2S\x9e{\xfa\x99\x99\u02d7.\xf6\xd7VWc\u01a2\xc8x\xbewjV.\x17<2\x89\x0e\x83\x87\x13\xad\xb5\xb1\xad\x18v$\x82\x15\tc\x99\x16\x8eTG\xfbV4\xa8\xb4(\x95\xa2*\xea@0\x89\x03K\xbed\u065b\x8f#\x1d\x87\xc1W\xdfA\xe7\x91c\x89DbT\xc7,\x82>\x83P\u0407\xd2\xfdh\x86N\xa0q\x13\x0emY\xcb\u053b.\xa3x\xdbw\bM\xc3\x1f\bZ\xad\xf3\xf3|C\x86\f\xfa\xfa\x85\x17^\x1a%\x84\x88\x1e<\xb8Ode\xe5*o\xb4{xb\xfe\v\xa4\xa4\xe4 M\x9bf\xfd\xdd\xe3{\xf7\xee\xea2y\xf2\xa7\x97-_\xbe\xbc]MM\xcd\xe0\x8a\x8a\x8a\xe4\xad[\xb7Q[[\x8b\xae\x1bdee\u04be};\xf2\xf2\xf2\xc8\xc9\xc9&++\x8b\x8c\x8c\f22\xd2INN&!!\x81P(\x84\xdf\xef\xbaGW\xcc\x15\x8ec\x13\x8b\u0148D\"D\xa3Q\xaa\xaa\xaa)++\xa3\xa4\xa4\x84\xe2\xe2b\x8a\x8a\xf6SX\xb8\x97\xed\u06f7\xb3\u007f\xff~b\xb1\x18\xc1`\x90\xb6m\u06d2\x9c\x9c\\\x15\f\x06\xbe\xee\xd9\xf3\xa4\xcdc\u01ce\xfd\xb8{\xf7\x93\n\x8e\xdf\xee\u077b\xb7\x93\x9f\xdf\xf6'\xd9W\x05\x05\xab\x985\u007f\xa1\xb8\xeb\xd6[\x15\xc0\v\x93\xdez\xf1\x93\x0f?\xbc~\xf5\u0295\u0122\x11e\u0162\xa2.\x0e\u07b2[\x1f\xce}\xe0Ur\xbbv%R\x15\u00caF\xb0\"\x11wE\xa7\xe3\xb8\xcd+\x94jP[\xd1Mo\x89\xda\x12\xcd\xf0#\xec\x18\x8b_\u007f\x94Ms>#\xf7\u013e\x8c\xb8\xe9A\x82\xe99T\xd7F0\f\x9d\u0120\x81\xe1\xf3#u\x1f\xba?\x00\x02f>\xfc\x1b\xd6|\xf6\xa6+\xe6\xfe\x80\u04f8qc}\xc8\xe0\x93\xf7>\xf5\xd4\xe3\x17\xe5\xe6\xe6/\xfd\xfc\xf3O\xc59\xe7\x8c\xf5\xc4\xdc\xe3g\x89\xe1\xed\x82\xefg\u0294\u027c\xf3\xce;\xa2i\xd3,u\xdc\xe3\xe7}\xfe\xf9\u0511\x97]v\xc5\u041a\x9a\xda\xd6;w\ue92a\xaa\x8aF\x8d\x1a\xa9.]:\x8b\x9e=O\xa2{\xf7\x1e\xe4\xe7\xbb\"\x9e\x91\x91Abb\xf2\xf1\x81\b@\x1d3\xc9W\xf7\xbf\x887np\xd3\xfa\xfe\xbe\xf2w8\xec\x8a\xfb\xc1\x83\a),\xdc\u02da5k((\xf8\x965k\u05a8\x8a\x8a\xaa\u4924\x843\xf7\xef\xdf\u007f\xe6\u0085\x8b.\xbc\xfc\xf2\xcbV\x8c\x181b\xf2\xc5\x17O\xf8\xa4\xee\xf9uB>s\xe6tF\x8d\x1a\xfd\xa3\uecd3N\xeau\xd4J\x037L\xbc\xec\xee\u28bd9\xd5\x15G\xce\u06bcy\xb3@\xf91c&\xa0(\\\xb7\x82\x99O\xdf\xcd\xd8\xfb^\xa2I\xf3\x1cl\xdbF7,\x94\xae\xa1\x94\x83-\x05R\xe0\x86\\\xe2\xcbF\x95\x02\xbf\xae\xa1\t\a\u007fr\n\x1dO9\x93\x03\x1b\v8\xb4e-[\xbe\x9eA\x971Wa\xe8\x1aR),[\xa1\xeb\x0eB\xd3Q\x8eE(5\x85\x8cV\x1d\xdd}-%RJ-\x12\x8b\x12\xae\xad\xcd]\xf4\xf57\ud065;v\xec\x10\r\xb7\xdf\xc3\xc3\x13\xf3\x9f1_}\xf5\x05\x8b\x16-\x12\xe7\x9e{\xfe\xd1\xc6\xf1J\xa5\xbd\xf9\xe6\xeb\x17O\x9d\xfa\xc5\x19\x0f>\xf8\xf0\xc9ee\xa5\xc1\xe2\xe2\x12\xfc~\xbf\u0763G\x0fN=u\x84\u07bbw/\x91\x9f\u07caf\u035a\x92\x94\u0538^\xb4m\xdb$\x1a\xad=&3\u37c9c\x1f\x15{\xb7\xff\xa5\xdf\xef\xa7E\x8b\x96\xb4h\x91O\x9f>\xfd9\xf3\xcc3(--e\xf7\xee=b\xe5\xcaUj\xee\u0739\xce\xea\u056b\u067auk\xcb\xed\u06f7\xb7\\\xbd\xba`\xf4\x84\t\x13\xee9\xf5\xd4\x113&L\xb8\xf4q!\xc4\x11\x80Q\xa3Fs\ubb77\x88!C\x06\xab\xd3O?\xf3G\u0747u\xf1\u007f!\xc4\x11\xa5\u0504CE\xfb>\x8aE\"\xa7\xed\u06bd\xcbM54-@\xb1i\xc1\x17$7\xcba\xf4\x1dO\xe3OL$b\x9a\b\xc3@H\x89\xa6\x1c\xa4\x12G\xd3\x14\x1b4\xa9PRa\x9bQ\xb2;\x9dD\xab\u07a7\xb0\xf6\u02ff\xb1c\xd9\x1c\x9au?\x99\xf4\xbc\xf6\u0122\x11L\xdb\x01M#\xa89 u\x90\x90\x9c\x99K09\x95hU9JI\x11\x8b\xc6,\xdbq|\xc5%\xa5\x1d\x00\xaa\xaa\xaa\xbd+U\x0fO\xcc\u007f\tTU\x95\x8b\xe4\xe4\u0506mox\xf6\xd9g\xae\xba\xf2\xca+\xee[\xb6ly\xf6\u07bd\x85\x84\xc3\x11\xb2\xb3\xb3\xcdq\xe3.\xd0/\xb8\xe0|\xa3{\xf7\x1edd\xa4\xe3\xf3\x05\x01\xb0\xed\x18\xe1p5J)4M\x8b\v\xb7@\xd3\xfe5\x9d8^\xf8m\xdb\u01b2,\xa4\x94\b!\xf0\xf9|\xb4h\x91O\x8b\x16\xf9\f\x18\xd0_\\~\xf9\xa5\xc6\xfa\xf5\x1b\x98:u\xaa3g\xce\\\xb9}\xfb\x8e\xa4\xfd\xfb\xf7w]\xb3fM\xd7\xf9\xf3\x17\\\xfe\xfa\ubbfd|\xe5\x95W\xdd/\x84\x90O<\xf1\xa4z\xe2\x89'9r\xa4T4i\x92\xf1\xa39R!\x04J)\xee{\xf0\x01\x9f\x10\xa2z\u03de\x1d\xf7\xff\xe5/\xcf\f\xa8\xae\xaeJ.--SJ)a\xdb6J)\x96\u007f\xf82\xfe\xe44\x06\xdf\xf0gT\xa0\x11\xb1\xa8\x89\xc2F)\a\x15oN\xd1\xf0\xe2\xa4.\xd5Q:\x0e\xbeP\x02\xed\x06\x8f\xa6p\xcd\x12\xcavof\xd7\xf2\xb9\xa4\u4dad\x0f\xc98\x8e\xc4r\x14>]\"\x1dE\xa3\xf4L\x12\x1a7\x89\x8b\xb9['\xecHy9\u06f7os\xdc\x10\xd4n\xefK\xe0\xe1\x89\xf9\u03dd\x993\xbf\u0493\x93S\x9d\xb8\xb3\f=\xf3\xcc3#\x97.]r\xe3k\xaf\xbd>\xa2\xa8\xa8\x88H$B\u01ce\x1d\x195j\xa4\x1a3f\x8c\xbf[\xb7\xae\x04\x02\t\xf1g;XV\xf4hIV\xfd\xa7\xab\x92P\x17\x86q\x1b&\xbb\xab\x1fc\xb10B\xb8\xae=##\x93\xa1C3\x192d\xb0\xbe{\xf7n\xfd\xf3\u03e72m\xda\x17j\xed\xdau\xe2\xc0\x81\x03\xcd\n\n\n\xee\x9d5kV\xffg\x9e\xf9\xcb\xf37\xdd\xf4\xfb9B\x88H\x93&\x19j\xe6\xcc\xe9\xfa\xa8Q\xa3\x9d\x1fs;\xcb\u028a\xed?\xdf\xf3'\xf2\xf2\xda,_\xb4h\xc1\x05EE\a\xfe\xb6d\u0252\x8cp8\xecD\xa3\x11\u0772\x1dP\x8aE\xaf>\x04\xbaA\x9f\x89w \x83\xc9D\xa2&8\x02\xa4[tK\b04\x81\xae\xc5\xc3N\xc2]d\xe4\xd8\x0e\xe9m:\u04e2\xd7\x10\x8e|6\x89}\xab\x16\xd0\xf2\xa4!4m\xd3\t\u06cc`\xa1\x10\x8e\u0110\x12\u01f2\t5iJ0\u0794Z\u0157\xf7G\xa31\x8e\x1c)\a\xa0\xbc\xbc\x9c\x993\xa72j\xd4\xd9\xde\x17\xc2\xc3\x13\xf3\x9f\x1b;vlc\xc0\x80\x93\u0168Q\xa7;\x00K\x96|3\xec\xe6\x9b\u007f\u007f\xc7\xe2\xc5K\x86o\u0672\x95H$\xc2\t'td\xec\u0631j\u0738\vD\xbbv\x1d\xc5\xd1\x10J\f\xc7q\xfe'>\x87R\x8aX,V\u007f21\f\x1f\xad[\xb7\xe3\xe6\x9bo\xe5\xe2\x8b\u01cb\xa9S\xa71y\xf2dV\xacX\xc9\xee\xdd{F\x14\x16\xee\x1dQX\xb8w\xee\u0085\xf3\x1e\x1d2d\u063cQ\xa3F;-Z\u42af\xbf^\xa8\xf2\xf3[\xff(\u06d4\x9e\xdeL-[\xb6X\xf4\xeb7P\r\x1at\u02ac7\xdex\xedV\u04cc\xbd\xb5r\xe5J\xddql\th\x96e\x03\xb0\xe8\xa5\xfb@)\xfa_u\x17\xaaq\x1a\x91#%8\xb6\u0114\uef02O\x13\x04\r\x1d]S\xf5\u92a6i\xa2\x05\x13\xc9:i\b;\x96\u03a6\xa2p\x1b%\x9bV\u04fcCg\x94\xa90\xa5\xc4q$\xd2qP\xd2\u0097\x90T\xbfx(\xfe\x12H\xa5\x88F#\x0e@ff3O\xc8=~\xb6\xfc*c\x84EE{\x99:u\x1a7\xde\xf8\u007f\r\xc50\xf9\xa6\x9b~{\xe5\xb6m\xdb\xfe\xbca\u00c6\u48a2\xfd\xe4\xe66g\xfc\xf8\xf1L\x980\x9e\u039d\xbb\u015d\xb0uL~\xf8\xff\xf4\xc1\x15\"\x9e\xf6\xe8\a\xe0\xc0\x81}|\xfc\xf1d\xde~\xfb\x1d\u05ad\xfb\x8e\xec\xec,\xbat\xe9R\u0561C\x87??\xfd\xf43o\b!\xaa\xea\x9e\xfb\xf6\u06d38\xe5\x94Sh\xd1\"\xff\xdf\u078e\xab\xae\xba\x92\xd7_\u007f\x03\x80G\x1ey\u8669S\xa7\u0774e\xcb\x16,\xcbR\xa6i\t+\x9e\x83\x0e\xd0\u007f\xe2\xad\xf4\x9dx;\x18A\xc2e\xc5\u0636\x8d\xe9Hl\x05\xba&\xf0k\xe0\xd3\xdc@\x8a\xa3\x14\xd2\x1f\xa2\xb6\xb2\x825o?\xce\ue15f\x93?`\x14C\xae\xb9\x93\xa4\xd4tjL\x1bt?\xc1\x80ABR\x12\xb6\x19e\xca\xed\x17\xb3s\xe9\x1c4]\xc7\b\x84\x9c\u071cl}\xf4\xa8\x11\x9f>\xf7\xdc\xf3\x13\x84\x10\x91\xc2\xc2]\xa2e\xcbV\xde$\xa8\xc7\u03ce_\xe5r\xfeo\xbeY|\x8c\x90/^\xbc\xa8\xef\x84\t\x17\u007f5s\xe6\u033f\u031a5;\xf9\u0421b\u018c9\x87\xd7^{\x95G\x1f}\x84\u039d\xbba\xdb1b\xb1p}\xbc\xfa\xe7\x80R\n\u06f6\xe3\xdb\x1d%;;\x97\xdf\xfd\xee\x0f\xbc\xf1\xc6k\xdcp\xc3\xf5\xc4b&s\xe6\xccM\x9e1c\xc6_.\xbf\xfc\xb2\xaf\u05ac)\xe8[\xf7\xdc\xcb.\xbb\x82\xa5K\x97\xfd(\xdb1n\xdc\xd16sw\xde\xf9\xc7\xdf\xf5\xee\xddkR\u02d6y\x18\x86!\x02\x01\xbf\xf2\xfb\xfd\xf5s\x03K\xdf|\x82yO\xddL\xf8\xf0A\x92\u049a\x12\xf0\x1b\x18\x9a\xc0\xd0\x04R*\xa2\x0e\x84-E\xad%\x89\xd8\xeed\xaa/)\x85f\x9d\xfbb\x84\x12(\u0771\x9e\x8a};\t\x04\x02\b\xe9\xa0\xe2\xee\u0736\x1d@\xa05\x98\x83\xd04!\"\x91(MR\x9bt\x06\xda\x03\u0636\ud578\xf0\xf0\xc4\xfc\xe7\xc0\xa2E\v\xc5E\x17\x8d\xaf\xbf\xff\xf8\xe3\x8f]\u007f\xd7]w\u007f2k\u05ac\x81\u06f6m'??O>\xf2\xc8\u00fc\xfc\xf2\x8b\x9cz\xeai8\x8eM$R\xf3?\x13N\xf9W\x91R\x12\x8d\xd6b\x9a\x11z\xf4\xe8\xc5\x13O<\u018b/\xbe@\x9f>}\xe4\xb6m\u06d81c\xc6\xc0[n\xb9e\xf2s\xcf=\xf3\x1b\xa5T\xc0\x15\xe1\xf1\u03181\xfd\xdf~\xef\xe1\xc3Oe\u0294\xc9\xf5\xf7\x9f}\xf6\xafw\x9e}\xf6Y\x85\xb9\xb9-\x10B\b\xbf\u07e7|>_\xfd<\xc0\xba\xa9o3\xeb\xa1\xeb)\u07bc\x8a`\xe3&\x84BA\fA\xbc@W}).\xb7\xe2\xa2\x02#\x10\"\xa5U'\x92\x9a\xe5R{\xb8\x98\x8a};\xb0L\xab\xfe\xaf\xed\xf8D\xaa\xb2-l\xdb>\xbaO\x14*\x10\nRU]\xb5\x1d\xd8\x1d\x0fQy\xae\xdc\xe3g\u026f*f>o\xdelm\u0420!2\xeeZ\x1b\xdd~\xfb\xad/\u007f\xf4\xd1\xc7\xe3\v\n\n\x00\x9cQ\xa3N\x15w\xdcq\x9b6x\xf0\x10\xc0\xcd\xe9\xd64\xad^d~\xf615!\x90R\x12\x0eW\x13\f\x069\xff\xfc\v\xe8\u05ad\x8b\xf6\xdcs\xcf\u02f7\xdf~G\u035b\xb7\xa0yuu\xcds\a\x0e\x1c\x1c\xa0\x94\xbaB\b\x11>\xed\xb4\xd1,]\xbaX\xeb\xdf\u007f\xe0\xbfu9r\xee\xb9\xe7S^^Fjj:B\x88\x92}\xfb\xf6^\\YY\xf5\xf1\xb4i\u04f2\x8b\x8b\x0f\t\xbf\u07e7\x84\x10\u00b2m\xa4\xe3\xb0{\xd6vD[\x00\x00 \x00IDAT\xf9\\j\x8a\x8b\xe8?\xf16\xf2\xfa\x8e !1\x89\xda\xdaZ\x1c\xe9\x80\x10\bM\u00f6lJ6\xad\xc0\xb1LR\xf3;\x92\u07be\a\x15\x85\xdb(\u07f7\x93hM9\x9a?\x19G)lGaK\x90\x910\x8e\x19\xab\xdf\x17\x80\n\x85B\x1c8pp\xa3\x10\xa223;K\xcb\xcd\u0353\x9e,xx\xce\xfc\u007f\x98\xd7_\u007fM\x1b6l\xa4\x04X\xb1bY\x9f\xf1\xe3/\x9a\xf5\u99df\x8d/(( 11Q\xddv\xdb-\xda\xdf\xfe\xf6\xb66x\xf0P\x00\xa2\xd1\b\xba\xae\xf3\xdf,X\xf5S\t\xba\xae\ub626\x89\u3634k\u05d1\xe7\x9e{Z{\xfa\u99f4\xbc\xbc\x96j\xe5\xcaUL\x992\xe5\xc2\xf3\xce;\xf7\xf3\x82\x82\xd5]\x00\xfa\xf7\x1f(?\xf8\xe0\xbd\u007f{\xac\xa4\xa6\xa63c\xc6W\x1a@nn\x8b%c\u01desf\x8f\x1e=\xf64i\x92\x86\x10B\x05\x02~\xfc>_}6P\xe9\xee-\xcc|\xf4&\x96\xbf\xf98\xe1\xb2\xfd$%7\xc6\xef\xf3#\xa4D\xd7t\x90\x0e\aV\xcfc\xcd[\x0f\xb3o\xe9t\x1ae\xb6@\xf7\xfb).\xdcAuy9\x9a\u03c7\x94\n)\x1d,\x05U\xe5e\xc4j\xea\x9aN\v\xea\u02a6\u06cek\xd7'\x8c\x1f\x1f\xb8\xf3\xce;|g\x9ey\x86~\xe3\x8d\xd7\ubbfe\xfa\xb2>g\xce,}\u04e6\rZ\xc3u\x02\x1e\x1e\x9e3\xff/\xf1\xc8#\x0f\x8b\xab\xae\xbaZ\x02|\xfc\xf1G\xe7>\xf1\u0113o-X\xb00\xe9\xf0\xe1\xc34o\x9e\xa3\xee\xb8\xe3vq\xe3\x8d7\x00:\xb6\x1dsK\xa8\x8a_\xf6\u0730\x10n\xd7\x1f\xc7q\xf0\xfbC\\u\xd55\"77\x97\xbb\xef\xbeG\xae^]\xa0UTT\x8e\b\x85\x9e^2}\xfa\x97\x13G\x8f>c\xcaE\x17],\x9f}\xf6\x19q\xd3M\xbf\xfb\xb7T\xed\xb4\xd3N\x97\xef\xbf\xff\x9e\x18?\xfeb5x\xf0)\u07fe\xfa\xea+cm\u06da\xf2\xed\xb7k\xf2\xcb\u02cfH\xc3\u0405\x10BX\x96\x1b\x12\x89\x85\xabY\xf5\xc1\xf3\x14o]G\xb7\xb1\u05d0\u0575?\xbe\xe4T\xccH-\t\t\x89d\xb4\xeeD\xe1\u0499\xec\x98\xfd1M\xf2;\xe0\v%RUz\x90\x8a\xcaJ\x82\xb9\x06J\xc4\xdcu\xb4B\x10\xa9\xac \x16\xae\x89\u007f~\x90\xd2\xc1\xef\xf7\xd3(\xa9Q-\xc0\x93O>\x15\xf9G\xdb=p\xe0@\xfdO\u007f\xba\x1b\u01d1j\xf8\xf0a\xea\xe4\x93\a(\xc3\b\xb2g\xcfN\xf2\xf2Z{J\xe2\xf1\xdf\xffN\xff\x92?\xdc\xef\u007f\u007f\x13\x8e\xe3\x88\xe7\x9e{^\x01<\xff\xfc_\xc7N\x9b6\xed\u0765K\x97\x85jjjh\u06f6\xadz\xfc\xf1G\xc59\xe7\x8c\x05\xc04#\xfcZ\x1dX \x10\x044\u05ae\xfd\x96;\xee\xb8S\u035a5[\xa4\xa44f\u0420A\x911c\xc6L\x988\xf1\x8aO\x01n\xbd\xf5\x16\xe1\xf7\xfb\xd5C\x0f=\xfcO\xbf\u01de=;\xf9\xeb__\u2a67\x9e\xe4\uaaef\xe6\xb5\xd7^\x03\xe0\xbd\xf7\xde\xed>u\xea\xd4OV\xacX\u046a\xa4\xa4\x04!4%\xa5\x8c/,r\x87\xa9t,\x92\x9ad\xd2\xfe\x941\xb4\x1dq.\xa9y\x1d\x11\x02\xecX\x94\xed\xf3\xa6\xb0i\xc6\xfbD*\x8f\xa0l\x1b4A\xff\xdf<F\x8b>\u00d0\xb1(J@Bj\x06{\x17\u007f\xc1\xdc\xc7~Cmy\x19\x86\u03cf\x12\x9a\u04f6Mk}\xe4\xf0\xa1\u04c7\x0f\x1b\xf6F\xd1\xfe\xfdM5M\v\xef\u077bw\xe5\xc9'\x0f\x8c\x0e\x1f>\xd40\x8c\xa0\x01\xec\xfd\x9e\x92\xc3\xc7T\xba\\\xb6l\t\xfd\xfa\r\xf0\x14\xc5\xc3\x13\xf3\x9f\x82\x1bn\xb8^\xbc\xf8\xe2K\n\xe0\xc5\x17\xffz\xfaG\x1fM\x9e\xbcr\xe5\xaaP$\x12\xa1[\xb7\xae<\xf9\xe4\x13\f\x1f>\x12)M\xea\xf2\x9d\u007f\u0543A\b\xfc\xfe\x10\x85\x85\xbb\xb8\xf3\xce?\xf2\xe1\x87\x1f\x92\x90\x90\xc0\xc0\x81\x03#\xe3\u018d;\u007f\xe2\xc4+\xbe\x02\xb8\xed\xb6[\xc5\xe3\x8f?\xf1\x0f\xcfz\xdf~\xbb\x9a\x1e=z\xfeS\xef]SS\xde\xea\xce;\xef\x992s\xe6\xac\x13\x8b\x8a\x8a\\\xb1\x94\x12\xa9\x14\b\x1d!t\x94t\x90\xb6EZ\xab\x8et\x18u\x11m\x06\x9fIJ\xb3\xe68\xb10;W-b\xeb\xbcO)\u07b8\x12'\x16\xa5\xf7\xb5\xf7\x91?\xe8\f\x84\xb4\xd1\x04$\xa5g\xb1\xe1\xb3W\x99\xf7\xd4\x1fp,\v\xe16\x0e\xc5\xd05\u0661C\a-'';~\xa5\"),,\\\u05e8QR$33\u04d7\x96\x96\xee\x03\xb9\xc3\xe7\xf3\xefh\u07fe}E\xf3\xe6\xcd\x17]r\u0265\x87\x85\x10[\xbe\xefs\xfc\xf1\x8fw1x\xf0 F\x8e\x1c\u5a4b\x87'\xe6?\x06o\xbe9IL\x9cxE\x9d\x90\x8f\xfe\xf0\u00cf\xdf[\xb5jUJ$\x12\xa5W\xaf\x9e<\xfb\xec\xd3\xf4\xeb7\x10\u02ca\xe28\xce/>\xac\xf2Cq\xcb\xf5&RZZ\xcc\x1f\xffx7\x93&M\xc2\xef\xf73`\xc0\x80\x8aK/\xbd\xf4\xe2K/\xbdl:\xc0G\x1f} .\xbc\xf0\xa2c\x04\xfd\xb3\u03e60f\u0339?\xe4=\xda\xee\u07bd\xa3\xe3\xea\xd5\x05r\xf7\xee\u0772\xb2\xb2Jj\x9aV2l\xd8\xd0\xd3\u05ee]w\xff\xfb\xef\xbf\u03d6-[\xb0m\xc7\xcd\"\x12\x1aB\xe8\b\xcd\x00\x01v4\x8c\x11\b\u0462\xe7`N\x18y.-N:\x05\xbdq\x06\xbbW-`\xc5\xcb\u007f\xa6r\xdf\x0ez]s/\xad\x86\x8dE\x03\f\rR\xd23Y\xfc\u04bd,\x99\xf4\x88\x1bc\xf9\xfe+0\xe5\x9e\xd3D})\x06M\xd3HJJ\"++\x93\x94\x94\x14,\xcb\u079b\x99\x99Y\x99\x9f\x9fw 77w\xe6%\x97L\xf8\xb2Y\xb3\xac\x1d\xff\xaf\xfd\xe9\x8d-\x0fO\xcc\xffEV\xae\\&z\xf7\xee\xa7\\\xd1\xf9p\xf4\xcb/\xbf\xf2\xde\xe2\u014bS,\u02e2w\xef\u07bc\xf0\xc2s\xf4\xec\xd9\a\u04cc\xd4\xd78\xf18\x8a\x94\x92P(\x89\x8a\x8a\xc3\xdcq\u01dd\xbc\xfa\xeak\xe8\xba\xce\u0211\xa7V\xfc\xfe\xf7\xbf\xbbx\u0108\x91\xd3\x01V\xaf^!z\xf6\xec\xf3\x0f\x1d\xfa\xaaU+\xfa}\xf6\xd9\xe7\xadJKK\xba$$$\x0e\u07bd{O\xb8\xac\xacL$$$\xe4\x80\u02a8\xaa\xaaR\xb1XL\u0555\xfe5\f\xa3\xa6}\xfb\xf6\xd9EEE\xbe-[\xb6P[S\x8b\xed\xb8\x95\x0fu\u0747\xa6\x1b\bMC\xd3t\x1c\xdb\u010eFH\xca\xc8&\xb7\xfb@:\x9c~\t\xca\b\xb0\xf4\x85\xbb(\u07fd\x99>\xd7\xddG\x9ba\xe7!P\x18~\x1f2Z\xcb\xfc\xc7\u007f\xcf\xf6E\xd3\x10\x9a\x86j\xb0V\xa0\xaeDB\x03\x01\x96\xc7=\xae\x1c\u01d1\x80\x1e\n\x055\x9f\xcfOVV&\t\t\x894j\x94\xb4\xbbC\x87\x0e\xbb\xf3\xf2\xf2>\x1f>|\xd8w\xbd{\xf7\xfd\xfa\xfb\xf6\xc5\xff\xfd\xdf\r\\{\xed5t\xe9r\xa27\xc0<<1\xff!\xbc\xf3\xce[\xe2\xd2K/\aP\xdf~[0\xfc\xfe\xfb\xef\x9f2}\xfa\x8cd\xd34\x9d\xee\u077b\ubbfc\xf2\"\xbdz\xf5\xf5\x84\xfc\a\nzyy\x197\xdf|+o\xbe\xf9\x96\x13\b\x04\xf41c\xc6T\xdd{\xef=c:v\xec4?\x18\fj\xd1h\xb4auI\u07c2\x05\xf3:~\xf8\xe1G\x83\x0e\x1e<x\xd9\xee\xdd{:\x16\x17\x17k\x9a&B\xc1`\x90h4\x86e\xd9\xc4bQ\x1c\xc7F\b\xed8!u\x17\x01%&&*P\"\x1c\x8e\xb8\xcd=\x00\xc3\xf0c\x18>4]\a\xb4x\x98\x04\xacH\x18P\xa4\xb4hKbz6\xe5{\xb7a\x9b\x11\x86\xde\xfa,\xad\x87\x9c\x8dm\x9a\x04\x92\x1aQ\xba\xa5\x80\x19\u007f\xbe\x92\x92m\xeb\xfeN\u033f/\xdcTGC\x87\x1e/ \xe6(\xa5\xb0,\xcb\xd14\xcd\u07e8Q\x12\xa9\xa9\xa9\x04\x83A'9\xb9QU\xabV\xad\xcb:v\uce22}\xfb\xb6S\x87\r\x1b\xb6*##\xb3\xf0\xf8\xd7_\xb6l\x89\xe8\xd7o\x80\x97\x1e\xe3\xe1\x89\xf9\x0f\xb9\xa4-**l\xf5\xdb\xdf\xfe~\xfa\xfc\xf9\xf3\xdbWTT\u0431c\a^\u007f\xfdU\xfa\xf7?\x19\xa5\xdc\x06\x10\x9e\x90\xff\xff\f\x8ex\f\xfd\u02112\xae\xbd\xf6:>\xf9d\niiM\x18:t\xe8\xc6\x17^x\xee\xf4\xa6M\xb3\v\xe3\xfb=\xf5\x81\a\xee;s\u04e6\u037f\u0674is\xcf\x1d;v \x84 \x1c\x0e7\x9cP\xae\xaf\u007fX\x97\x1e\xf9}\xf9\xfbu\xc7\xd00\x8c\xfa\x15\xac\xb6m\xbb\xee\xdc\xf0\xa3\xeb\x06\x9a\xa6\xe3\u0583\xaf;\xf1\xd8H\xcbD\xd3\f4\x9f\x8f\xa4\xa6\xd9\xf4\xbb\xf2.\xd2\xdbt&RSE \x94\xc8\xc1\x8d\xabX\xfa\xda\x03T\x97\x1e\xa8\xaf\xeaX\xf7\x15h8\f\xea\xab4\xba\xa7\xa7\xef\x1d#\rJ\xfc\"\xa5T\x80\xd0u\xb7\xcd_rr2\x81@\x80\xb4\xb4&t\xea\xd4\xc9NII\xf9\xa8m\u06f6_\xe6\xe7\xb7Z}\xd6Yg\x954,\x99\u0423G\x0f\xe3\xba\xeb\xaea\xe4\xc8\x11N^^kO\xdc=<1\xff\x9e/[\xfa\x84\t\xe3\u07de7o\xfe\xe8C\x87\x8a\xc9\xc9\xc9\xe1\xf9\xe7\x9f\xc3\xcdZq\x88F\xa3\x9e\x90\xff@4M\xc3\xe7\v\xb2c\xc7V\xae\xb9\xe6:\x16,XHNN6#G\x8e\xfcj\u04a47\xcf\x02|\xbf\xfb\xddM\x9f~\xf3\u0362\xd1\x1b7n\"\x163\x8f9\x14\xc4c\xd0J\xb9\xa1\xea\xba\xfd\xfe}9\xfc\r\ufeee\xdd}\t\xdbvk\xe1h\xba\x0fM3\x10\x9a~\x8c\x007\x8c\x81\vM#\u0538\t\x89M\x9ab[&V,\x82t\x1cb5\x95\x84+\u0290\x8e\xddP\xad\u007f2|>\x1f\xc1`\x90`0H\xeb\u05ad\xc9\xc9\xc9.MKK[\x92\x96\x96V\x90\x92\xd2x\xfem\xb7\xddQ$\x84\xd8[\xf7\xf7o\xbd\xf5\x866r\xe4\xa92;\xbb9\xf3\xe7\xcfe\xe8\xd0\xe1\xde\xe0\xf3\xf8\xf5\x89\xf9\x8c\x19_\xb1p\xe1\xd7\xe2\xb1\xc7\x1eW\x00\xf7\xdcs\xf7\vS\xa6|z\u00e6M\x9bHHHP\x8f?\xfe\x98pk\xb1(\xa2\u0470'\xe4\xff\xe4\u054ea\x18\x18F\x80\xe5\u02d7r\xf5\xd5\u05e8\r\x1b6\x8aN\x9dN\xe0\x8a+&>\x9e\x94\x94\xd4\xf4\xd9g\x9f\xbb|\xf3\xe6-\xff0\xad\xb3n\u007f7lX}\xb4\xd6\xfb?h\u05a1\x14R)\xb7T\xadRnHL\xd3\u30ae\xd7\u05c9o(\xca\xf1P7B)\xecX\fG\xd9?H\xb2u@\xd3\x05\xb6\xa3~\x12\x897\f\x03\xbf\xdfG(\x14\"33\x93\x9c\x9c\x1c\u0574i\xd3\xe5\x19\x19\xe9S\u018e\x1d\xb3\xec\u44c7,=\xfe93g~\u0168Q\xa7{\x03\xd0\xe3\xd7%\xe67\xdex\x03/\xbc\xf0\"\x00\xaf\xbe\xfa\u0288\x17_|q\u06bau\xdf\x05\x95R\xfc\xe1\x0f\xbf\xe7\xd1G\x1f\xc6\xe7\xf3y\x8e\xfc\xdf\x10tM\xd3\xf0\xfbC|\xf8\xe1\xfb\xfc\xf6\xb77QZZF\x9f>\xbdcyy-\x03\v\x16,\xa4\xa4\xa4L\x89x\xe7\x88\xe3E\xfd\x18\xc7]\xe7\xa4\x1b\x8a|\x03\x1b\x0f\n%]!\xff\u01c36^b\xa1>\xde\xee\xde\xfc\t\x894JoF()\x19_(\x01_ \x88\xe1\xf3\xe1\xf3\x19\x84\xfc\x06\x86\xc0\x9dh\xd5\x04\x89\x8d\x92\xc9h\u0782\xe4\x8cl\x84\xe1\aM#\x1251-\x13G\xbaW\x03V\xa4\x96HM5\xb5U\x15T\x94\x1e\xa2d\xefN\x8e\xec\xdbE\xac\xb6\xa6~\xfb\xea?O\x83\xab\x83\xff\xd7IM\b\xd04\x9dF\x8d\x1a\u047auk\xd2\xd3\u04cb\xf3\xf3\xf3\xbe>\xf7\u0731\x8b\x86\r\x1b\xf1\x8a\x10\xa2>G\xf6\x92K&0b\xc4p\xe2s@\x1e\x1e\xbfl1\u007f\xe2\x89\u01f8\xf5\xd6\xdb\xeb\xbeDY\xa7\x9f~\xfa\xeco\xbe\xf9\xa6suu5g\x9cq:o\xbc\xf1\x1aM\x9bf\x11\x0eW\xff\xa4M#~\xe9H\xa9\xf0\xfb}\b!\xb8\xff\xfe\ax\xf8\xe1G\xd1u\x9d\xe4\xe4d\x94\x92\xaa\xb2\xb2\xaa\xbe{\xd0O\xb1\xf0J\xd7\r\x8c@\x00_ \xc1m\xd4l\u06d8\xd1\bB\bB\u0269d\xb4\xea@\xeb\x01\xa7\u04b4M\x17t\xbf\x1f_ \x80?\x10\xc4\xef\xf7\x91\x14\xf4\x91\x92\xe0\xc3\xf0\xb9\xd5\x195\x01\x9a/@(9\x15\x15\x00G\x82\xe5\x80%\x8f\x9eT\x1c\a\xa4\x05\x96\x19\xc32\xa3\x985U\u0516\x1f\xa6\xa4p'\xfb\xb7\xac\xa1\xe8\xbb\x15\x14\xef\xdcL\xb4\xaa\x1c+v\xb41\x89\xe6\xaau\xbc\u035d\x02)\xff\xce\xed\xc7E]*\x05~\xbfO\xcb\xcf\xcf'//\x8f\x13N8a\u0168Q#\xdf\x1e1b\xd4K\rO\x80\u007f\xfd\xeb\xb3\xe27\xbf\xb9\u024b\xa9{\xfc\xb2\xc5|\xc1\x82\xb9\xe2\x94S\x86+\x80K/\xbd\xe4\x91y\xf3\xe6\u0771\u007f\xff\x01\u06b5k\xab\xde~\xfbM\u0477\xef\x00\"\x91\x9a_L\xb1\xac\xff\xae\xa0KB\xa1D\xca\xcaJ\xb9\xfa\xeak\xf8\xfc\xf3\xa9\xca\xef\xf7\x8b\xc4\xc4$\x1c\xc7&\x1c\x0e#\xe3\xcd$\xfe\x91\xa0\a\x02A\xfc\xc1 >\u007f\x00M\xd7\x11\x9a\xee\xe6\x91\xeb\x1a\x9an\xe0\v\x04\t&5&)-\x83\x94f9$7\xcb!9\xad\x19IM\xd2\t5N\xc5\x1f\b\xa1\xa4\xc24c\u0516\x97Qqp?\xc2\xf0\x91\x9a\u05d1\x94\x96\xed\xd0\x02!l\xc7F\x17`h\x1a\xba\xa6\xe1\xd3\x05~C\x03MG\x02\xb6\x84\x98\xa3\x88\xc6Lbu\xd50\x95@\x89\xba/\x85\xdb\xcdH\b\xf7\n\xc0gh\x04|\x06\x01\xbf\x0f\xa5 RSM\xb8\xe20\x87\xf7\xed\xe2\xc0\x96\xef\u063f~\x05%\xdb\xd6Q[v\x88XMe}\xa6\x8c\xd0\u2379\x1b\x84\x8b\x8e\x17uM\x13\xd2q\xa4\r\xf8[\xb5jEnns:w\xee\xfc\xdd\xc9'\x0f|l\u0738\xf1\xef\x1f\xbf\xff\x16/^$\x06\x0e\x1c\xe4\t\xbb\xc7/G\xcc\xdf\u007f\xff]\u018f\x9fP\u007f\xff\x85\x17\xfez\xfeK/\xbd\xfc\u0386\r\x1b\x83III\xea\xe9\xa7\xff\"\xae\xba\xea\xea\xff\xa9n@\xbf\x14\x02\x81\x04V\xadZ\xc1\xd5W_\u00fau\xdf\u0468Q#\xfc~?\xd55n\xa9`\xa5\x14\xb2\xc1>Oi\x9aI\u04fc\xb6\xb4\xec\u0503\xdc\xf6]H\xcbjNb\xe3\x14\x94\xe1\a\xcd@\t\x1d\xdd\xef\xc7\x1f\bb\xf8\xfdh\x86\x0f\xdd\xe7G\xf3\xf9\x11\x86\x0f\xc3\aq\xcdwO*\x8e\xeb\x9c\x1d\x1b\xccH\x14\u04f2\x89Y\n\xd32\xe3B.\xd04\x1d]\xd7PB\xc3A\xb8\xff+0\xa5\xdbrNA}\x8c\\*7n.\x04\b\x14*^[W\x13\x02\x05\xe8\xc2-\xb7+P\u8e86\xe6\xf7\xe3\xf3\xfb\xdd\x05L\xa6\x83\x19\rS]\xba\x9f\xd2-k8\xb0v\te\xdb\xd7S}h\x1f\xe1#\x87\xb0\xa2\x11@C7\f\x84\u0410\xd2q;\x1f\xa9\xefM\x8dTB\b\x91\x99\x99I\u02d6-\xe8\u0673\xe7\xa6\xe6\xcds\x1e:\u55213{\xf7\xeeS)\x84\xa8\u06e9\xdaSO=a\xb4i\xd3\xda:\xfb\xec\xb1\xea\xd5W_\xe6\x9ak\xae\xf3\x06\xa6'\xe6?_\xbe\xf9f\xa18\xf9\xe4!J)\x95w\xe1\x85\x17\u03181cF\x87\xea\xea\x1a\xfb\xf2\xcb/7^\u007f\xfd\x15t\xddO4Z\xeb\xc5\xc9\u007fd\xdcf\x12\x06\x0f?\xfc\x10\xf7\xdcs\x0fR*\x02\x81\x00\x8e\x94\xd8\xf1\xaeA\t\x8dS\xc8l\u0749\xdcN\xdd\xe9|\xf2\bZu\xefO\xa8qj}\u03b6\x03H\xe2m>\x05\xf8}\xf5\xd1\t7\xf4,\xe3\xa1\x0e\x89[\xf6\xf68\xc7_\xefr\x85\xc0\xb4$\xb5\x11\x13+\xbe\x92\xd7\xd0\x056\x1a\xb6\x128\b\x1c\xa9\xb0\xe2o(\x94B9\xa0I\xe5\x164w\x14\x9a\x12h(\xa4\x00\xa1@\xb8\xadG]a\x8f\x8b<B 5PB 5\x81\xd0\u0709\\\xddg\xa0\xf9\xfch\xba\x86\x9b.i\x11=R\xcc\xe1\xed\xeb8\xb8~)\xa5;6PV\xb8\x8d\xaa\xe2\xfd\x98\xe1Zw\xf27\x9ev\xa9\xbe\xbfc\x95\x02\x84\xcf\xe7#%%\x85\x9c\x9c\x1c\u06b6m\xbd\xa7U\xabV\u04db4I\x9bu\xdbm\xb7\u007f#\x84(\xaf\xfb\xe3'\x9f|\xdc8\xf3\u0333\x9c\xf6\xed;xn\xdd\x13\xf3\x9f'\xf1\\_\x01\xa8\x9bn\xfa\xcd]\u04e7\xcf|h\xfb\xf6\xed\xb4i\xd3FM\x99\xf2\xb1\xe8\u06b5\xfb\xaf\xbap\xd6O\x89a\x18\u8e9fg\x9f}\x9a\x9bo\xbe\xb5\xfe\xca'\xb1q\n9m:\u04b4mgZv\xebO\xeb>CH\xcf\xcd\xc3\x10`9\xee*O\xe5H\xa4\x94X\xf1IN\r\xf0\xf94|\xba\x1e\x9f?\xfc\xfb\x8c\x12\xe9\x06\xa0\xe3\xf3\xa6\x02\xd5`\xf0*\x14Q[Q\x13s\x889\nG\xba\xae\xdav\xc0\xb6\x95+\xe0\x8eB\xd9\n\xddQ\xe8\x12\x84T\x88:{^\x97O\x1e\x0f\xaf(\x11?\x9b\xc4\xdfO\xe1\u693b\xb1pp\u217f\x10\xf1\t\xd0\xf8k\b@\xf3\x19\x88\x80\x0f\xcdo`\xf8u|\x86\u00ac,\xa5l\xc7:\x0em_\xc7\xc1\xed\xeb8\xb0m\x1de\xfbw\xe2\x9814\xddp_K\xc9\xf8\xa4\xef\u07fbu\x9f\xcf \x18\f\x91\x92\x92B\xeb\u05adHKK\x9bw\xc2\t\x1d\x17\x0f\x1atr\xc1\x88\x11\xa3\xbe\xa8\xfb;]\x0f\xb0e\xcbwL\x9d:\x8d[n\xb9\xd5\x1b\xa4\xbf\xc6\xef\xe5\xcfu\xc3\x1fy\xe4a\x03\xb0W\xae\\\xde\xe6\x96[n\x9d\xb8c\xc7\x0e\x00\xe7\xd2K/\u047bv\xed\x0e8\x9e\x90\xffT\x0e@\b\xa4t\x1dx\xe3\xe4d\x8e\x94\xbbF1-\xbb\x05g\xdd\xfc0-\xfb\f!f\x81m;\x84\xc3Q4%\xd1\\I\u018a\xbb\xdeX<n\x1d4tt\x04\x8e\xaa\x13T\xf7\u007f\xd9@\xb4\x1b\xaa\xbbj`?\xa4\x02\u06c2\u06b0$\x12\x95X\x96D:\x80\xe3\n\xb8\xee(\x84\xac\x13\xdc\xf8\xeb\u01c5\x17\x15\u007f\\\xa8\xb8h\xc7?[\x831#d\xfc\xf7u\x8f;\"\xfe{\xf79\xc4O\f\xc4\xdfCDM\xa8\x8e!5\rS\xd7P~\r#\x90L\x8b\x8e\xc3h\xd5e\x04fu\x19\x87vm\xa0hK\x01\xbb\xd6~\xcd\xee\xf5\x8b\x89\xd6V\xb9i\x9a\x9a\x86\xae\xdcYQ\x85\xac\xdf \u02f2\xb1\xacj\xc2\xe10\xc5\u0147\b\x85B\xc3v\xed\xda5l\u02d6-\\r\u0244\x0f\x87\x0e\x1d:\xf3\xf2\xcb'~.\x84\xa8l\u06f6=\x00w\xdey;'\x9ex\"\x17^x\x917X\u007fE\xfc,\xd3;\xde}\xf7\x1dn\xbc\xf17\xd2u.\xbe\xdbW\xacXqfee\x15\xbd{\xf7\xd2\xee\xbf\xff\u03e4\xa66\xc1\x8cw\x94\xf1\xf8i\xae\x8a4M#';\x8b\xe6y\xf9\x1c\xae\x0e\xb3\xbfp7\x8em\u046a\xcfP\xd2\xdbt\xa2\xa2\xb2\n\u02f6q\x1c\x85&\x14\xfex\b%\xe2(\"\x0e\xd4\xda\x02[\t|\xbe\xbae\xf2q\x01W\x02\x89\xeb\x80\x1d\xe5:c\x89\xfb\x98\x02\xa4\r\x8e\xa9\x90\x11\x87X\x8dCm\x85Em\xb5\x83S\xeb\xa0E$ZL!L\x85\xb0\x14\x9a\x03\"\x1eR\x11\xc4\xe3\xe0u\xd1\x19D}k\x96\xfa\xe5\xfa\x9a\xe6.VB\xc4\x17-\xe9\b\x8d\xf8\xfd\xa3W\x03u7M)W\xc4U\x83\xeb\x85\xf8\x95\x80\xe6H\x94\xed\xe0\xc4lb\xe1\x18\xb1p\f\xb4\x00\xa9Ym\xc8\xed\u0417\x96\x1d\xfb\x93\xd9\xe2\x044M\xa3\xa6\xbc\x183V\xeb\xeeW]C\x13\xfa\u07dd\xc5T|i\xaa\xe3H\x8a\x8b\x8b\u0556-[EUUe\u78a2\xa2s\x16/\xfef\xd0\xf5\xd7_w\xe4\x8b/\xbe\xd8\x02\xb0x\xf1\x12\x06\f\xe8/f\u035a\xed\rVO\xcc\xff\xb7)--\x13{\xf7\xee\xe5\xeb\xaf\x17v\xfd\uaaef\x9e/(\xf86\xa8\xeb\x1a\xf7\xdcs7\u00c7\x8f@J\u06db\xf4\xfc\x89\x91R\x92\x96\x96\u0189={!\xd2r\xf9f\xde\\\xaa\x0f\x97\x10\r\xd7\u04a2\xc7\x10\x02\xc9M0-\x13S\xbaU\v\x13u\x81)\xa1\xca\x12T\xdb`)H\xf0\xe9\x04tw\xa5\xa7\xac\x8bn\b7\xce]'\xecn\xefN\x894\x152,\x91\xd5\x0eN\x8d\x83Y\xe3P[e\x13\x89H\x94\xa5\xa8W\xfb\xb8\xf3\x16\xf1e\xf9\xaa>D#\xdc\xfct\u074dq\xfb\xfc\x01\x8cP\x10\u007fB\b\u007f(\x84\x91\x10\xc4\x17\f\xa0\a\xfd\xe8\x01?F\xc0\x8f\x11\xf2c\x04\xdd\x14\xc7@(H !\x84?\x18\xc0\xef\v\xa0\x1b\x06\x1aZ\xbd\x90\u05c9:\u01c9=\x8eB\xd8\xeee\x84c;D\xa3\x11L\xd3$\x98\x94F\xb3\xfc\xae\xb4\xe8\u060f\xacV\xdd\x10B#Ru\x183Z\x8b\x94\x0e\x9a\x86\x9b\xe9s\xdcITJ\x89\xae\xebB\xd7u*\xca\xcb\xed=\x85{\xb4\xe2\xe2\xe2\xdc={\n/\xbc\xe8\xa2\v;,^\xbcd\xf9}\xf7\xddW=k\xd6l\x1e~\xf8Am\u07bc\xf9\xde\xe5\xa9\x17f\xf9\xdfd\xc1\x82\xb9\u0525\"\xbe\xfd\xf6[\xa3\u05af\u07d0\x020`\xc0\x00F\x8d\x1a\x15\xbf4\xb5\xbc#\xfb\x1f\xc0\x8eE\x89\xe0#\xbf[_\xfa\x8c>\x9f\xd9o=\u02eeU_\xb3\xe7\xdb\xc5t8\xf5B\x1ca\xe0H\x87\x04\x1d\x92\xfd\x10\x8b\x82\xa3\x14\x8e\x04\xbf\x00\xbf\x13W]\x9fpSFp\xc5W:\xb8jo*\x94%Q\x96D8\xa0\x1c\x85\xb2%\xa6\xa3\x88\xd8no\xcf\xfa\x90\b\u2a17mPwE\xf7\xf9\xd1\xfd\x01t\xbf\x1ft\x81\xe3XH'\x86\xe9DP\x96\x85f[H3\x8cY[\x89\x15\x8b\xe2\xd8\x16\x8e\x1dC\xd96B\xd30|\x01|~?~\u007f\x10\u007f\xb0\x11>\u007f\"\x9a\x1e\x00\u0347\x1e\xf2\xe1\x0f%\xa2\v\x03iK\xac\x98\x89\x8a\xc5pl\u06dd\xb9=\xbaA`\xc7C?\x9a@\tI$Z\t\x9a\xc0\x1fJ\xa7\xcdIg\x91\xd3~ E[\x97\xb3i\xc9\x14\xf6o[IeY\x11\x8e\xed\x8ec\xa1\u05657R_\xafF\xd34t]7\x84\x10r\xff\xfe\x03\xce\xe1\u00c7}ee%\xe3\x0e\x1c8\xd8w\u07bc9w\r\x1b6\u20fb\xee\xba[\xe6\xe6\u62bd{\xf7*/\t\xe0W\x10\xfe\xfc\xb9n\xf8\xfb\xef\xbf\xdb\xea\xe5\x97_\x99\xbft\xe9\u0496Bh\xbc\xf2\u028bL\x9cx\x95\x97\xbd\xf2\x9ft\x02\x02\xaab6[U23\xe6\xaf\xe0\xc5\xdfM\xa0\xacp\a\x9dF\x8c\xe5\xf4\xbb\x9f')=\v'\\C\xcb$A\x93\x00\xec\xa8V\x14V+b&$*H\xd4\xdcE6\x9a_C\v\xb8\x82ET\xa2\xa2\x12,\xe9f\x95HE]B\x9e\x9b#\xae\x88\u064a\x98T\u023a\xd4\x17\x11\x9f\xbcT\x12)\x14\x9a\u03c7\x16L@\xf3\xf9\xb0c\xb5\xc4\"\x15\xc4j+\bW\x16SY\xbc\x9b\x9a\xb2}\xd4\x14\xef%r\xf8\x00\u044aR\xccp\r\xb6\x19E\u019bI+\xe5\x06\xf6\x15\xc4K\b\xb8\x8e^\xf7\x05\t&&\x93\xd0$\x83\xa4\xf4l\x92\u04f3Ii\x9aG\xe3\xb4<\x12\x933\t\x06S\b\x05\x1ac\xe8A\x1c\xd3\u008a\x85ql;\xbeJ\u050d\xe9\u0539w\x15\u03d0Q\x024C\xc7\b& \x84\x86\x15\xae\xa4\xb8p=\xdbW~I\u0476\u5517\xee!RS\x8eR2^`L\x1c\xb3}\x9a&\u0404@JG*%\xb5&M\xd28\xf1\xc4nj\xc0\x80\xfew\xdc{\xef}\x8f\xd7\x1d\xab\xfb\xef\xbf\xcf\xf8\xdd\xef~\xeb$'\xa7zN\xdds\xe6\xff[l\u0738\xf1\xe2\xc2\xc2\u0096\xb6\xed0d\xc8\x00\x86\r\x1b\x06\x80\xe3H\f\xc3[\xe9\xf9\x9f@\x01~C\u00e8\x8e\u04aa\xf3\x89t\x1dz\x16\xf3\xdf\xfc\v{V/\xa2t\xfb\x062\xb2\xb2\xc0/\b\u06cajKP\x1e\x83\x98\x03\xd2V\xf1\x15\x92n\xf8A\x85\x1d\xa4\x16?\x01[\n\xe1\xb81\v\xd5\xc0mK\xa9\xea#)\x86\xe1\xc6eL\xa9\u0710\x8cP(CG\x0f5\xc2\b\xf9\x89\x85\xab\xa8,\xd9N\xd5\xc1]\x1c\u07b9\x8e#{6pd\xcf\x16j\x8b\xf7aEkp,\xeb\u01e9\xb3%p\xcb\x05\x04\x13I\xcdjE\xd3\x16'\xd04\xa7\x13\xe9\xd9\xedHMoIJZ\v\x92R\u04f1b1\xa2\xd1p\u072d\x8b\xfaP\x90\x92\xee\x0fR9\u011cj\x84a`\xf8\x12h\u07a6\x1f\x99\u037bQQ\xbc\x8b\xa2\x1d+8\xb8\xb3\x80\xb2\x03[(/\xd9E\xa4\xb62^hL;f;t\xdd\xd0\x04\x8a\xf2\xf2\n\x16/^\"\xaa\xaa\xaa\x1e\x9b8\xf1\xf2\x93\a\r\x1a\xf4\xc8\xe5\x97O\\&\x84\xb0\xff\xf4\xa7{\xf9\xec\xb3O\xb4s\xce9W\n!\u0639s\x1b\xad[\xb7\xf3\x06\xb2'\xe6\xffy^y\xe5eq\xed\xb5\xd7)\xa5\x94\x187\xee\xc2\u02cf\x1cq\xb3(\xce8\xe3\fZ\xb4\xc8'\x1a\xadE\u05fd\x95\x9e\xffI1\xf7\xe9\x1a\x8d5\x8b\u018d\x1a\xd1i\xd0\b\n\xbe\xfa\x90\u0292\x03l_<\x936\xbd\ac\x18AJ\xc3\x11bRa\xd5\t\xb8\xed\u01b9\x95r\x85]\xd8\xf1\xec\x10#^|K\xa8\xfaKF7Y$\x9eQ\"@\xd7\xdc<r\x9fO\xe0\xd3\x05\x8e\u07cf\x13\fPc\u0654\xec\xddJ\xe9\x8ao)\u077c\x9a\x92M+9\xb2c#v\xb8\xfa{\xb7=\xadI\x13\xb2\xb22IKK\xa3Qr#\x1a'7&\x18\n\xe1\xf3\xf9\xea\xcb\x16(\xa9p\xa4C4fR]\x1b\xa6\xa2\xb2\x8a\xc3G*(;|\x98\xe2\xe2b\xa2U\xe5\u0626\x85mV\x10\xa9\xfa\x96\x03[\xbf\x05\xc0\x1fJ\xa2YnGrZ\x9fDv~\x0f\x9a\xb7\xe9I\x93\u0336h\xc2G,\x1a\u01b6\xad\xa3\xb5g\xe2\x93\x05\x02\x81t\x1c,;\x8c\xe9H\x90\x92Fi\xb9tn\x9aG\xfb\x93N\xe7\xc8\xc1\xed\x1c\u06b3\x86\xa2\xed\xcb\u067f\xb3\x80\x9a\xca\xd2x\xa3\x0e\x83\xba*1B\b|\x01\x1d)\x1d\u05ae[O\xd1\xfe\x03g\x14\x15\x15\x8d(((\xf8`\u035a\u056fw\xef\xdes\u02581\xe7I7\x12\xe5u@\xf2\xc2,\xff\x03\xfc\xedo\xef\f}\xfc\xf1\xc7\xe7\xad_\xbf\x81\x13N8\x81\xbf\xfd\xedmz\xf4\xe8\xe9\xd5_\xf9/\xa0\v\xa84\x15\xab*|\xec*\x8f\xf0\xf6\xdd7\xb0r\xda\xfbdw<\x91\t\xcf}FJ\xf3<\xaa\xaak\xb0l\x85\x13U\xc4b\x12\"\x12\u007fD\x11D\xe1fZ\u01d7\xcf\xeb\u008d\xdd\xc4S\x05\xad\xb8s\xf5\xe9\x1a\xba!\x10~\x81\xf0k\xe8!\x1fzb\x00-\x045\x95\x11\xb6,\x9b\u01f6%s9\xb8~\x05e[\xd7 \x8f\xcbd\xd2CId\u7de5]\xfb\xf6tn\u05ca\x0e\xadZ\u0432y\x0e\xe9\x19\u9924\xa4\x90\x94\x94DRR\x12\x81@\x00]\xd7\xe3c(\x9e\xc5\x1e\x8fQG\xa21\xaa\xaak(\xaf\xac\xe2Hy9\x87J\xca\xd8{\xe0 ;\v\x8b\u0631k7[7nd\xff\xaem\xa8\u0631}\x9f5\xcd \xb7m/Z\xb6\xefK~\xc7A\xe4\xb6\xeb\x87?\xd4\x18\xd3r\x1buH\xc7F\xc5\xeb\xa6+w\xd9\x04\x8ec#\x1d\v\xa5\x1c\x84r\x1b\xa8\xe8\x86\x1f]\u04e8)?\xc0\xa1\xddk\u0675a\x1e{6/&\\S\xee:uMw\xb3n\xe2a\x17Pn\x88G@vV3:w\xeet\xa4g\u03de\x9f^{\xedUogdd-\xae\u06fe6\xed\u06b2`\xde\x1crs\xf3\xbc\xc1\xec9\xf3\xff\f\xaf\xbd\xf6*W_}\r\x00\xabW\xaf\xba\xb0\xac\xac\f\x80\x01\x03\xfa\u04ed[Wl;\xea\t\xf9\u007f\x01\xa9 \xc9'h\uace8JkL\xbb^\x83(\x981\x99\u00c5;)\\\xb3\x94F\xd9y(K\xa0*m\x88J\xfc\xb6DJ\x81\x94\n\xd3Q(\x01\xbav\xd4UH\x01\xb6\x06\xb6\x00\xe1\x13\x84\x82:FPC\xf84\x8c\x84\x00F\x82\x8e\xee\x87\xca\xe2#l\xfeb\x1a[\xbf\x9e\u039e\xd5\xdfP]v\xa8\x81E\xd1I\xca\xc8&\xbf[O\xba\xf4\xeeO\x97N'\u042du.\xeds\x9b\x92\xdb,\x1d\xdf1Y\"\xeer\xd3\xfa2\x04R\x1e\x93\t\xe5f\xc1\b\x12\x13B$7J\xa4yv\xf61\x1e(\xec8\x1c,;\xc2\u03bd\a\u063ck/\xeb7nf\u00f7\xab\u067af\x15\x15\a\x8b\x90\u04a6p\xeb2\n\xb7.c\xdd\xe2\x8f\xc8m\u04d3\xb6\xddF\xd0\xf6\xc4\xd3HHi\x8e\xed\xd8\xd8f\xbc\xf3R]\x9c^\xb9\xce\x1c)\x91\xca\x01%\xb1cQ\x04\xe0\x0f\xa5\u042a\xebH\xb2[\x9dD\x8bv\xfdY\xbf\xec#\x0e\x16\xaeG\xc5\xdb\xeb)%\xe2\xb1t\r\xc3\xe7\a\xa5\u053e\xfd\x87TIiY\x93]\xbb\xf7\\\xb5i\u04e6\v\xfe\xfa\xdc3\xcf\xff\xdfon\xfa\xb3\x10\xc2\u06b1m;W_}\x9d\xf8z\xd1B5x\xd0\x10o@{\xce\xfc?K\u07fe}Vm\u0630\xb1\xa7\xa6i<\xff\xfcs\\r\xc9e\xde\xc4\xe7\u007f3\xd4\"\xa0$\xaaXU\x1d\xa2\xe0\xbbM\xbcy\xebe\xec\xdb\xf8-=\u03ba\x94\xb3\xeex\x05\xabV\x11\xad\b\xe38n\n\x9f\u012d\xaf\"l\xe5\xe6j\xc7\xf3\xb8\x95\x00\xe9\xd3\x10\x8du|\x89:\x81\x90\x8e\xcf'\xd0\f\x83@\xa2\x0fM@eq\x19\x1bf\u007f\xcaw3>\xa2h\xc3jb5\xf1\xc6=\x9aARF\x16\xf9]{\xd1u\xf0H:u;\x89\xbc\xdc\x1c\xba\xb4\u0320u\x8aA\b\x00\x1b\xcb4\xb1l\xc7\x15\xbc\xffG=\xf5c\xbb\x11\x1d\xfd\xacuY%\xa0\xd0\x04\xfc\u007f\xec\x9dw|UU\xba\xfe\xbfk\x97\u04d2\x13\xd2h\xa1\x86^D@)\n\x88\xbd\x01\x96\xb1\x97k\x1d\xb1a\x1f\xcbXf\xece\xf4\u07b9\xe3X\xc6^\u007f\x8c]\xb0PT\x8a\x02\x82\bH'\xa1CBB \xbd\x9c\xba\xcb\xfa\xfd\xb1\xf7>I(\x8ewF$\xced}>\x87\x84\xe4$\xd9\xe7\ucd5e\xf5\xae\xe7}\xde\xe7\xd5T\x15M\xd7A\xe8H\xa02nR\xbc\xbb\x8a\xcdE%,]\xb6\x8c\x85\xb3f\xb2j\xf1Bj\xcb\u02fc\u07c0/\x10\"\xaf\xfbP\x0e\x1d}\x01}\x0f\x9f@Zf\a,\xd3&\x11\x8f8]\x93l\x13\xcb4\x90\xb6\x89\x94n\u027f[\xf4$\xa5\x8dD\xa2\xeb\x014M\xa5z\xd7fV-\xf8;\x9bV~I\xa4\xa1\nE\xf3\xa3*\x9a\xa3\x9dW\x85\xfb\xde\n\xa4\xb4\xedx<&\xc3\xe9ij~~w\x86\r;l\xe6]w\xde\xfe`~~\xef\xef\x00F\x1d9J\xfc\xfe\xee;\xe5\u99df\xd9:\xa9[#\xf3\x037\x16-Z\xc0\x91G\x8e\x01\xe0\x95W^>\xff\x89'\x9e8\xa4\xa1\xa1\x811cFs\xec\xb1\u01f0\xbf\xf6^\xad\u35cb\u03b3\xfd\x82\x8e\t\x83.}\x06\xd2}\xc8H\x8a\xd7\xfe@\xc9\xea%Tm\\OV\x87~(\x8a\xc3A;\x05\x94N\xc1\x8e\xd4@Z\xb8\x05C\x8e\u02e0/\xa8\xe0\xcf\xd0\xd13T\x046\x9a\xee#\x10T\xa9/\xaff\xf5\x17\x1f\xb2\xf4\xe37(Y\xb7\f3\xe1P)\xbeP\x98.\x87\x1c\xc6\xe0\xa3Of\xd4\xf1\xa7\u043bO\x1f\xdae\xa6\xd19\x03rT\b\xd8\x06\x981L[\xe2)\x19\u007f\xea\tn\xafNH\xce\x17\x9b}\u0352`%\r\x14a\xa0\nAn@#\xb7k;\x86vm\xc7o\xc6\f\xa5\xf2\xb7\x17\xb1rM\x01\xd3?\xff\x9c93g\xb2~\xdd\x1a\x12\xb1\x06\xb6\x15~K\u0676\x15\xac^\xf8\x1e\x83\x8e\xba\x88^\x87\x9eLZF{\x8cd\x8cD,\xd1\xe8\xb4h7z\xd1x\x1d\xed\x04\n\x86\x11\u01f6u\xb2;\xf6c\u0504\xdb\xc8\xcd\xeb\xcd\xea\x85\xefS\xb9k\v\xb6\xb4P\x15\x1d\xdbvr\x11\x8a\xa3\x84W\xfc\xfe \xd1XB\xae][(jjjOI&\x8c\xe3f\u0318v\u07a9\xa7\x8e\xffd\u1885\xb2\xbc\xbcB\x00\xf2\xb9\xe7\x9ec\u04a4I\xad\x13\xfb\xd7Fy\xfe\x1a.\xf2\xd5W_K}\u07b6m\xdb\x1b\n\n\n\x8e\xa8\xafo\xe0\xf4\xd3O\xe3\x82\v.r\xb4\xc3?\u04a0\xb7u\x1c\u0623\x9d\r\xf8\\\xaa\xa4\x16\x9d\xf2\xcaZ\xd6/\x9aM\xb4\xae\x86\xbc\u0783\xe9\xd4{\b\xa6ab\xdb\x0e I!\x90\x9a\xc0\xf6)\u062a+\xd3S\x05\xfe\x80B\u042f\xa2\xa9\xa0\x06T\x02\xe1 \u04b0\xd9\xf8\xedl\xbe\xf8\xcb},x\xebi\xaaK\xb6a[\x16\xbe\xb4\fz\x1fy\x1c\xa7^\xfd;.\xb9\xe3\x01N\x9fp\"c\xfav\xa4\u007f\xb6\x8f.\xbaA\x86L\xa2\xd9\u03bc0\xed\x03\xdd$\x8e\x94\xfb\xa2e\xd9H\xdb\x00\xdb@U \xcd\x1f\xa0g\xe7<N9\xee\x18\u019f6\x81N]z\xd2\xd0`PQ^N<VOME\x11[V\u03e1|G\x01\xc1\xb4\f2s\xba\xa0\xf9\x02\x98F\x02\xe9\xedt4\xda\x10H\xaf\xbb\x92\x14H\xdb\xc62\x92h\xfe\x10\xed\xbb\r\"\xbbC>\xd2J\x12\xa9)#\x11m@(\x9a#\u007f\x942\xd5<[Q\x15!\x84\"kjk\xc5\u039dej\xf9\xee\xddg\xdes\xcf\xef\xd7|\xfc\xf1\x94\xf5\x9f~\xfa)\xd3gL\x17\x17_t1\bv\x99\xcf\x00\x00 \x00IDATO\xff\xf5if\u0398\xd9:\xc1[\xc1\xfc\xe7\x1dEE[\xf9\xdf\xff}\x1a)e\x9b\xbf\xfd\xed\xf9{\u05ed+\xe8\x18\x0e\x87\xe55\xd7\\-\x06\r:\x14\xcb2Z}X\x0e2\xa0\v\xc0\xa7\b\"\xb6N\xbd\b\xb2f\xfeWT\xef,\"\xb3}Wz\x1fq2\x96e9\xf2BM C\nvH%\x99\xa6`\xe9\n\xc2r\x8a\x88\xfc\x9a\x82\x8aDS5\xfc\x19!\xcawlf\xde\xcb\u007f\u2aff\xdeO\xc9\xdaeH\xdbB\xf1\a\xe9?\xf6\x14&\\{'\xe7\xdf|\x0f\xa3\x8e>\x82\x1e\xd9A\xfa\a-rE\x02\xd54\x1c\xe3*<;\x80\x83C=y\xc0nY\x06H\vE\x856\xe1\f\x8e\x18q\x18\xc7\x1cs\n9\xb9\xf9\xd4\xd4\xd4SV\xba\x1d\xcb2\xa8,\xdb\u0226U_\x11m\xa8\xa0MN\x17\xda\xe4vv]\x15\x8dT\u0751l${R\x04\xa9Db\xdb&RB\x9b\xb6]\xc9\xeby8\xe9m\xdaa&\"\xd4W\xef\xc42\x9dF\xd7N\x929E\xbb\bEQe}}\x83\xa8\xa8\xa8\xd0*+\xab\u03bc\xf9\xc6\x1b\xd7L\x9f1c\xfd\xe4\u0253\x99\xfa\xc9\x14q\xf9e\x97\xb7N\xecV0\xff\xf9\u01e0A\x87\xf0\xc9'\x9fq\xec\xb1c\xc7~\xf8\xe1\u01d7\x97\x94\x94\xa6\xf5\xea\xd5S\xdcr\xcb\xcd\xe4\xe6\xb6u\x16L\xeb8\xa8C\x02>\x15,\x14\xea\xf40k\x16\u007fK\xe9\x865\x04\xc2\x19\xf4\x1du\x12\xc1p\x1a\xaa_\"\xd2U\x94t\x15\xcb'0\x85@*\x02\xc5\x02\xdd\x06](\x04\x02!\x04\x925\xdfLa\xfa\xd3w\xb1j\xc6\xfb$c\x11\x00\xf2\x0f\x1b\xc3\xe97\xde\xc3\xf97\xdd\xcdQ\u01ce$7\x18 \xcd6\xc9S\x93d*f\x13?\x97\x83\x03\xe2\xfb}o\xa4t73\x03\x81Mvv:\u00c6\re\xe0!c\xc9\xca\xe9Bee\x05U\x95;1\x93qJ6/\xa5l\xfbJ\xd2\u04b3\xc8\xed\xd0\x1bE\xd3\xddJ\u0426\x15\xa5\xa4<\v<'I\xc7\xc2\xc2@\xf3\xa5\u046e\xeb :\xf7\x18B\x9b\x8cl\xea\xabJ\xa9\xafs\xc4\x02\x8a\xa2:\u0564Bq\xc4C\xaaB,\x1e\xb7*++}UUUgN\x9cxU\xe1\xec\u0673\v\xde}\xf7=\x1ex\xe8\x01q\xe7\x9dw1y\xf2\xe4\xd6\xc9\xdd\n\xe6?\xdf8\xf7\xdcs\xc5\xc7\x1fO!?\xbf\xc7y+W\xae<\xa3\xa2\xa2\x82\x91#Gr\xcd5\x13QU\r\xd3l\x05\xf3\x960\x14@U\x15\xaa-\x1f\x9b\xb6l\xa5\xe0\xdb\u0668\xbaN\x8f#\x8e!+\xbf;>\xbf\x81\xeeW\xb0qx\\\x81\xdb\xf4AJt\xa9\x92\x9e\u0786H\xed.\xe6\xbd\xfb\x14\xb3_\u007f\x88\x8a\xed\x1b\x01\bf\xb5\xe5\xc4\xcbo\xe4\xd2{\x1e\xe3\xb8S\x8f\xa3mZ\x10%\x91D\x97&\x1d|6\xed\xfd\xb6\xe3\xef\xf2\v!\xb8\xa2(h\x9a\x86\xae\xebh\x9a\xf7\xd0\xd045\xd5\xd1j\u007f'E\xdbv\"iU1\xe8\xd0>\x87C\a\x1fI\xbf\xfe#\xf1\xf9\xd2).\xdaB<\x1e\xa1\xbe\xba\x94\xe2\r\x8b0\x8d\x18\xb9\x1d\xfb\x11J\xcf\xc62\x93N\xe5g\xd3>\xd6{6\xb5\xb6\x9d\x1e\xa7\x96\x99$\x18\u03a5k\xcfat\xea\u069fD\xa4\x9a\xdd;7!m\x1bU\xd5R\xbdS\x05\x02EU\x95D<aU\xd7T\xfbjjjO\xbf\ubbbb\xea\xa6M\x9b\xf6\xfd\xd7s\xbff\xf2\xe4\u027c\xf1\xe6\x9bb\xea\u0529\xad\x93\xbb\x15\xcc\u007f\x9e\x91\x97\x97'\xbe\xff~\t=z\xe4\xffv\u04e6\xcdCjjj\x12g\x9du\xa6v\xf2\u0267\"\xa5\xd5j\xaa\u0542\xf8\x16]\x11D\xd1(\xad\x8e\xf0\xc3\xdc\xe9\xc4\xebk\xc8\x1f6\x9a\xbc\x81\x87b%\x93XR\x90\xb0\x04\xba\xea\xd02B@(=DZ DY\xe12\xbex\xf1\x0e\x96\xcex\r#\x11\x03\xa0\xef\x91\xc7\xf3\xdb?>\xc5o.\xbf\x8a\ue773Q\xa2\t|\xb6I\xa6.\xe9\x14\x90\xe4\xfa$\xaaH\u016d\at\xf8|>4\u034f\xaaj\b\xd7\xd9PJ\xdb}H\xb7\xb7\xa7\xe6\xf4+\xd5t4\xcd)\xe0\xd9\xef\xe2SM\xfc~\x83\xee\u077a2d\xe8\x18\xf2{\f\xa2\xbc|\x17\xa5\xa5[I\xc6\x1b\u0631y)uU\xc5d\xb7\xcf'\xabm7\xc7j\xc0\xb6HeB\x9bR/\x82Ta\x95\x94\x12#\x99\xc02mr:\xf6\xa4k\xfe`\xccX\x1de%\xeb1-\xd31\x1b\x13\x8d-\xedTUU\x12\u0244U]]\xed\xab\xaa\xae:\xf5\x81\a\x1e\xe8>\xe5\xe3\x8fg<\xf8\xe0\x83\xd6\u0529Sy\xe6\xd9g\u014c\xe9\xd3[\xe7w\v\x1f-^\u0372y\xf3\x06\u0473g\x1f)\xa5\f\x9dt\u0489\ud28b\x8bIK\v\xa9\x83\x06\rr#\x9d\xd6\xc4g\u02e1\x13@\x936m\x03\u042dG/\xd2s\xdaS\xbei-\x95E\x9b\x10@\xd4\x14\xc4-\a||\x8a@\x15\x92@z\x1a>\xbfB\xe1\x97\xd3\xf9\xea\xd9\xfb\xd9Q\xb8\u0519\x98iY\x9c}\xd9U\xfcv\xd2\rt\xcb\xefJ<\x9eD$\"\xa4\x05\x14\xc2\x1a\x844\x89.R\x01\xe9\x01\xa5H\xfc~?B8K\xa5\xba\xba\x82M\x9b6\xb3z\xf5j\xb6n\xddFEE\x05\x91H\x04]\xd7i\u06f6-\xf9\xf9\xdd\x190`\x00}\xfb\xf6\xa1]\xbbv\xf8|\x8e(2\xb1G1\x91t\x9b\\\xf8t\x81\xa64\x90\xdf\xddG\xe7\xff:\x8d\x11\xc3\a\xf1\xea\xab\xcf\xf1\xe6\x9b/\x90\x887\xb0\xee\xfbO\xa8\xad(b\xec\xe9w\x90?\xf0X\xe2\xf1(\x89D\xa4\x11\xc4]\x95K3^I\b\x14T,\u06e4\xa6\xa6\x92P\xb8#\xa3'\u0702/\x98\u0392\xf9\uf4ccG\xf1\aB\xa8\x8ak?\x8cDU55\x96HXK\x97\xfe\xa0J)/\xaf\xaa\xaa\n\x02\x17\x00\xdcx\xc3\r<\xf3\xec\xb3\xdcx\xc3\r\xad\x93\xbc\x15\xcc\xff\xf91g\xce\\\x01\xc8m\xdb6\xf7\x8dD\"=M\u04e4c\xc7\x0eb\xe0\xc0\x81\xad`\xde\xd2\xc0\x1c'!\x97\ud0ce\xedrh\u06f5\xa7\x03\xe6;\xb6QW\x17#j\xab$-\x13]\x11\x98\x96M(\x14\x00i\xb0x\xf2\xeb\xccy\xe91jv\x16\x03\u042d\xff\x10\xae\xbf\xedn~{\xe9y\xe4\xf8 a$0\x83NoO\x95\xc6f\x12\xf2\x00'8\x9d\x06\xd6\xe9\x80Ea\xe1Zf\xcc\xf8\x82\xa9S\xa7RX\xb8\x9e\xfa\xfa:\x12\x89d\xb3\xf9'\x84\xc0\xef\xf7\x93\x96\x16\"??\x9f\x13O<\x91q\xe3Ne\u0108a\xf8\xfd!\xe2\xf1H\xeayMA]QUTa\x11\xf0'\x181\xbc;C\x06?\u0148\xe1\x83x\xfc\xf1\xc7\u067c\xb9\x90\x92-\u02d9\xf6\xd6\xef8\xf6\xcc\xdfs\u0211\xe7\xa2(\x82x<\x82\xeb\xf4\xd2\xecd\x84\xe7\xad.\x1c\xe2K\u0692h\xb4\x81`(\x97#N\xb9\x01\u0757\u0192y\xef`\x1a\x06\xc2\xe7kTZJ\xd0\x14U5MS~\xf7\xdd\x12\x11i\x88\x9c\xff\xf0#\x8f\x88\xfb\xee\xbd\xf7b!\x84\u0675k\x17\x15\xb0\xa6L\x99\xc2o~\xf3\x9b\xd6\xc9\xdeJ\xb3\xfc\xdfG\u06f6m\xc5\xf2\xe5+d\u06f6m{~\xf7\xdd\xe2\v+++\xdbt\xef\u078dI\x93&\x89\xb4\xb4tL3\xd9z\x17[\x16\u04c2OU\xa9L\n\x96\xafX\u0156\x1f\x16\x92\x9e\u06d1\xbca\xc7!\x82\x19\x98\x96\x89%%\x81@\x00U\x1a\xcc\u007f\xe3\u007f\xf8\xea\xb9\ai\xa8*\a`\xf8\t\x13x\xe2\xcf\u007f\xe1\xca3O \xa4\x82iD\x11\xb6Ds)\x19\xb7\xc7\xc4\x01\x05q\xaf\xf9F \x90\u01ae]\xa5\xbc\xf8\xe2K\xdcw\xdf\x1fx\xf7\xdd\xf7\u063au+\x91H\x04\u06f6\xf1\xf9|4iE\xdaX\xfa\x1f\x8bQZ\xba\x93\x05\v\x160e\xcaTv\xec(\xa1G\x8fnt\xec\xd8\x19!\xc0r{\x95\xee\x19\xa9;\x15\xa8&>\x9f\xcea\x87\r\xe6\xb0\u00c6\xb3~\xc3V\x8a\x8b\xb7\x92\x885P\xbc\xf1;\xd2\xc3Yt\xee1\x14E\u0571,3\xf5\xa6\xefS\x13/\x1b?1M\x03\u0557F\xe7\xfcCQ\x05\xec,ZC\xd2H8\xfe.\x1eG\x83@(\x8a\x90H\xb9{W\xb9\xa8\xae\xae\x1eX^Q\xdeo\xf6\xacYS\xfb\xf5\xebg\u03593G\x1d7n\\\xabl\xac52\xff\xe7F8\x1cV\x00[Q\u0120\xf4\xf4\xb4.\x00\x9d:u\x12\xa1P\xa8\xf5\xee\xb5\xd0\xe8\\`\x93\x1d\xd2\xe9\u0439\v\xa0PWUNmu%9\xb9\x9d\x90R\x12Ho\x83\x99\xa8g\xce\x1bO0\xef\xf5\xff\xc1t\xbdT\x8e=\xe7\x12\x9ex\xe21F\xf4\xec\f\x18$\x13\xc6>[\xc7\x1dX\xaaH\xba\xc9L?\x8b\x17/\xe2\x91G\x1ee\xe6\xcc/R\x1e\u22a2 \xa5t\xf9s\r\xd3\xf5?Q\x145U5\xea\xd9\x02H)\xa9\xa9\xa9\xe1\x85\x17^d\u039c9<\xf6\u0623\x9c}\xf69\xa8\xaaJ<\x1e\xdf/\xa0'\x12Q4Me\u0318\x91<\xfb\xcc\v<\xf0\xe0\xc3|\xfe\xf9;D\x1bj\x98\xf5\xc1#\u0636\u0270\xe3~\x8b\xaa\xa84D\xebphtoW\x91\xcd{\xeb5\":\xf1D\x84\x80?\u0110\xa3/\u00d6\x92\xef\xe7\xbeE\"\x1e\xc5\xe7\x0f9\x85Tnd\xaf\xaa\x9a\xb0\xb1\xe4\xbau\x05B\xd5\xd4\xf3\xc2\x19\x19H)/\x11B$g\u03de\xad&M\xd3:\xf5\xe4\x93['{kd\xfe\u007f\x1b\xfd\xfb\xf7UV\xacXi\x0f\x1c8p\xdc\xe6\xcd[\x8e\u077d{\xb7y\xdcq\u01ea\xa7\x9d6\x01U\xd5Ze\x89-qR)\x02C\xd5X\xb1a;\x8b\xbf\xfa\x1cEQ\xc8?\xf2D\xb2\xba\xf6D\xd5|\b\xcb`\xd1\xcb\x0f\xf3\xed\xebO9v\xb4B\u5b097\xf1\xa7?=\xcea\xdd\xdac\x9bq\x92\xc6/\x9f\xd4\xf6\xf8qU\xf51s\xe64n\xbc\xf1&\x16,\xf86\x05\xe2\x9e\u007f\v\x80i\x9a$\x93\xc9\x14h{\tPUUS\x80\xdfT\xd1RYY\xc5\xe7\x9fO#++\x93\xe1\u00c7\xa3\xaa\xaa\xbb\x11\x88\xfdP<\x12)M:uj\xcf\xe0\xc1G\x11\x8dI\n\nV\x10\x8f\xd5S\xbcy\x19i\xe9\x99t\xeb3\x02M\u04f0\\\x9d9{)hd\xaai\x87\x87\xeb\x96e\xa0\xeaA:v\x1d\x88\"MJ\xb7\xaf\xc60\x92NR\xd7;\xfdH\x10\x8a\"\x84\xa2\xc8]e\xbbD}]\xdd@\xa1\xd0y\ua529\x9f\xbc\xf5\xd6[R(\xaax\xe0\x81\ax\xef\xbd\xf7Z'{+\x98\xff\xf4\u0467O\x1fu\xed\xdau\xb6\xdf\xef\x1fQZZzb}}\xbd\x9c0a\xbcr\xdcq\xc7\"\x84lU\xb2\xb4@\x9aE\x11 u\x8dU[\xcaX0}\nf,B\xaf#O\xa0\xeb\xd0C0c&\u07ff\xf1\x14\u07fd\xf6\x84\xa3\x9fV}\\t\xe3\x1d<\xfa\xd0\xfd\xf4o\x1f\xc6H\u01b0\xec\x83s\x92\xd7u\x1dE\u04595\xebKn\xbc\xf1f\n\n\n\xc9\xcc\xcc\xc4\xef\xf7\x13\x8f\u01db\x81s\xd3(\xbd\x11\x80\xed\x94U\xc0\x9e\xf3RQ\x14\x92\xc9$\xb3f\u0362]\xbb\xb6\f\x1b6\u048d\xec\x8d\xfd\x02\xba\x94`\xdbI\xb2s2\xe9\xdb\xef\b\xa4\xf4\xb3b\xc5w$bu\x14m\\JFf[\xba\xf7\x19\x01B`Zf3,oz]\"\xa5g\u013d6\x03\xcd\x17\xa2C\xb7C\xc0JPV\xbc\x163\x99L\x9d.\x9a<U\b\xa1\xc8\xd2\xd2RQ[W7\xf4\xa1\x87\x1fJ~\xf4\xe1G\vV\xad\\\xc9\xdau\xeb\xc5\xfb\x1f\xbc\xc7\x1bo\xbc\xd1:\xe9[\xc8h\xd1\xe6\u07f1X\x84\xf7\xdf\xff\xc0\x06H&\x93i\x91H\x04EQ\xc8\xca\xcaF\b\xb5\xb5\xea\xb3\x05\x93-> \x10\f\xa1\xea:F\xb4\x1e;\x11!\xe0\x83\x95\xef?\xcf\u04b7\xfe\x1b\xcbH\x82\xaas\u044dw\xf0\xf0\x03\u007f\xa0WN\x90x,\xc2A\xc2q7\xaa\xf6\xb1aC!w\xdf}\x0f\x1b6l$\x1cN\xa7G\x8f|\xc2\xe1\xf4\xbd\x12\xed\xd9\xd9\u0664\xa7\xa7\xa7~\xb6\x11(-L\xd3\xdc\u06e4\xcb\xe5\xe1\x93\xc9$\xbf\xff\xfd=\u03181\rP\xd0u\xfd\x1f\x9c\x16\x04\xc8\b]\xbb\x84\xb8\xe6\x9a[\xf8\xedUw\xa3\a\u04895T\xf2\xe5\xfb\x0f\xb3z\xf1T\xc2\xe9Y\xa4\x87\xd2\xd1u\xdd1\xd9J\xa9\xf8\xd9\xef&\x11\x8fGA\vq\xf8qW0\xec\xe8\vP5\x95d\"\x86e\xd9)\xf0O9\t\b!\x97|\xbf\x94\u0253\xdfy\xe4\xed\u0253\xcf\x03X\xb3z\x85\xfc\u04df\x9e\x14\x85\x1b6\xb4N\xf7V0\xff\xc7c\u04e6M*`I);\x1fr\xc8\xc0\xe3\xea\xeb\xeb\xd0u]\xe4\xe4d\xe3T\xbd\xb5*YZ\x1e\x8c;\xc5;\x01 '\u034f\xe2v\xc41j*X\xf3\xf1T\xbe}\xe9\x11\xe2\xf55\b\xdd\u03f97\xdc\u0243\x0f=@\x8f\xcc F\"\xbeW\x03\xe3_r\xf8|~\xa2\xd1z\x1ey\xe4Q\x96.]\x86\xcf\xe7#++\x9b\xea\xeaj\x8a\x8a\x8a\xf7z~CC=\x86\x91\xdc+\n\xb6m{\xafy)\x84H\x15\x13\x01\xd4\xd4\xd4p\xd7]\xbf\xa7\xb8x\x1b\xaa\xea\xfb\t&q\x82\xa0?N~\xf7 \xd7]w+\xd7^s\x1f\x81`\x98\xba\xea\x9d\xcc\xfc\xfb\x1f\u0670\xf2KBi\x19\xa4\x05\xd3\xf0i:\x9a\xaa!\x14\a\xd0=P\xf7\xca\xf9\x9b\x02z\"\x1eE\rf1\xe2\xe4\xab9\xfc\xd8\v\xd0\xfd\x01,\xd3\xc0\xf6\x1a`\v\xc5K\xae\x8ah,\xce\u04a5\u02d4\x8f?\x9e\xfa\u03b4\xe9\xd3\u007f\x030c\xfa4\xa9\xbb\xcd`J\xcbv\xb6N\xfe\x83<Zt\x02t\xf7\xee\xdd\u07a7\xc1D\"\x99fY6~\xbf*22\xc2{-\xa2\xd6\u0442\x00\u0775km\x17RP\xa4\x03l\xeb\xe7M#V\xf7\xff\x88\xd68\xa5\xe5'\xfd\xd75\xdc~\xcf}\xf4\nkX\xc9(\xf6Atcv\x80V\xe1\u33e7\xf0\u99df\x01\x90\x99\x99I\"\x91$\x16\x8b2t\xe8\x10\xbav\xedJVV&\xa1P\x88\xb2\xb2],Y\xb2\x84\x1d;JP\x14e/\xf0\xde\x17g\xee6`\xc60\f,\xcbb\xf5\xea5<\xf7\xdc\xdfx\xe2\x89?\xe1\xf3\xf9H$\x12?\x1aIK\x01>=J\xe7N~\xae\xbcr\x12\x91H=o\xbe\xf1\x14\x95\xbb\xb60c\xf2}\x9c}\u074bt\xea>\x04a[\u0130\x91\xb6\x8d\xad\xe0\xf6\vu\xb4\u4379P\x91re\x8cE\x1b\b\xa5\xb7a\u0109\x13\xc1\x96\xacX0\x05\xcb4\x10\x9a\xee\xde\x11\x05\x90h\xba.kj\xeb\xc5\xf7\xdf\u007f\xafdee\xbe\xbbf\xed\xdas\x0f\x198\xf0\u04de={\xb5v-j\x05\xf3\u007f<\xaa\xaa\xaa\xbcO\x8d\x86\x86\x86$8\xf6\xa5>_\xa0\x15\xcc[\xf0\xf0\xa0-S\x93\xe0&\xa8\xb7.[\x90\x02\xbd\x11\xa7\x9e\xc5\xed\xf7=\xc0\x90v\x01l3\x86y\x90o\xa3\xae\xeb\xd4\xd6V\xf1\xfe\xfb\xefS[[K\x9b6\x19\b!\xd04\x95+\xaf\xbc\x82\x1bo\x9cD\x8f\x1e\xbd\x9bD\xdf\x06_~\xf9%\x8f=\xf6\x04\xdf~\xbb\x10UU\xb1,\xa7C\xa9\xa7\x86\xf1d\x8a\x9e\xc2\u0176m\xd2\xd2B\xa8\xaaB$\xe2\x14\x10\xbd\xfb\xee{\\~\xf9e\xf4\xeb7`\x9f\xfe\xe9\xcd\xdeS\x1b\x14\x05B\xc18\xf9\xddC\\w\xedmTW\x973\u58d7(\u0776\x8a\xd9\x1f<\u0339\u05fdB \xad\r\xa6i`\xa9\x16\xa6\xe54\xbd\x964\xa9P\xa5\x89\xf2\xc5\x05\xf5x\xa4\x81P8\x8b\x91']\x8d\xb4,V,\x9a\x8ai&Q\x15\xcd\xdd\xe8\x1c\xe3\x05]\xd7eyy\x85\xf8\xe6\x9by\xbe\x8cp\xf8\xb5\xfa\xfa\xfa\x13\xc3\xe1\xf0r!D\x13O\x81\xd6\xd1J\xb3\xecc4\x8dVL\xd3LE8\x9a\xd6\xda\xe7\xb3eG\xe6\xce\xc7t\x1d|\xba\x9a\xa2\x1f\x00\xfa\x1d6\x92\xbb\xfe\xf8\x00\xa3{d\xa1\x99qLK\x1e\xe4k\x95\x80\xca7\xdf\xccc\u0672\xe5\xf8|:\xa1P\b\u02f28\xed\xb4\xf1<\xfe\xf8\xa3\xf4\xe8\xd1\x1b\xdb6\xb0m\x03\xcbJ\xa2(\n\xa7\x9c2\x9e\xc7\x1e{\x94~\xfd\xfa\xb8\xba\xf1\xc6\xdf\xe9\xa9^\xf6\xe4\xd2\x13\x89d3\xcae\xfb\xf6\xed|\xf8\xe1\x87xA\xca?\xdc$m\x87\xfe\b\xf8c\xf4\xef\x9f\u036d\xb7\xde\xc3\xf0\x11'\x00\xb0n\xe9t\xbe\xfb\xeaE\x14EC\xf7\x05\xf1i*\xba\xab\xac\xf1\xeco\xc1\x05w)\x91\xee\xc6#\xa5\xd3X:\x1ai@\x0fd1\xf2\xe4\xab\x198|\x1cB\x82i$\x1b7\x18\x97?\xf7\xf9\xfc\x94\x96\ucd27M\x9f\x91\xf3\xd0#\x8f\xbe!\xa5\xec\x04\xc8W^{\xad\xb5\xcdW+\x98\xef\u007f$\x93F\x93\x89\u072aZ\xf9\xb5\x8d\xdc\xecl\xb7\u035a3\xdaw\xcc\xe3\xc1\a\xefg\xc2\x11\x83\xf0[IL\xcb>\xe8\xe1\x9c\a\xac\xdf}\xf7\x1d\xa5\xa5\xa5\x84\xc3a\x84P\xc8\xcb\xeb\u0239\xe7\x9eC \x90F<\x1e\xc10\f\f\xc3\xc04M\x12\x89\x04\xb6m0f\xccXN>\xf9\x14\xb7xho\xde|O\xaa%\x99Lb\x18f\xb3\xa4\xe7\x9c9s\x89\xc5\x1a~r\xc3\f\xe9\xf2\u067a\x16a\xe4\x88n<\xf0\xc0\xe3t\xeb\xd6\a\x80\xf9\u04dee\u00ca/\b\x04\u00e8\xaa\x8a\xae*\u8aa3\xbaAx}Mid\xd2]\x91\x8b\rH\xcb\x01\xf4@(\x97Q\xa7\\G\xff\xc3N\u0136\r\x92\u0244\xdb|Z\xa4\x1e\xbaO\x17%;J\xf8\xfc\xf3\xcf\x0f}\xf4\xf1\xc7\u07d6R\xe6^u\xe5\x95\xd6\v/\xbe\xa4\xae\u07f0\xbeu\u2dc2\xf9>8 M\xdbk\xd1Ii\xbbG\xda\xd6\xd1b'\x95\x17y\x16\x15QSS\xedF\x9e\nw\xdd\xf9;\u039bp*\x8a\x11\xc30\x8c\x83\x0e\xe4RJt]g\xf7\ue76c[\xb7\x0e\x80\x9c\x9c\\\x84\x10t\xed\u0695\xbe}\xfb\x02\xd6>\xf9`Oz8l\xd8\xe18\t\xf9\xc6!<WB\xd1L\xe3\x97\xea/\xda4j\u07fcy\v\u02d7\xaf\xe0\xff\xa2\x12\xf6|]\x14\x11g\u0729\xc3x\xe8\xe1\xc7\b\x85\xc2D\xeb+\xf9\xe6\xd3?SS\xb1\r\x9f\xdfQ\xdah\x8a\x82\xae\n\x14\xa5\xd1\xcf\u073b\x1e\xa5\xc9\x03\xe9h\xdb#\x91\x06\xd22:2z\xfc\r\xf49\xf4\x18,3\x81a$\x91M\x1cpl[\nU\xd3\u5dad\xdb\xf9\U0008bbce\xfd\xcb\xd3O\xdf\x02p\xed5W[\x93&\xdd$\xcav\x95\xb5.\x82V0o>\xd2\xd3\xd3R\x9f\xfb|\xbe\xd4\x02\xf4\x16Rk\u04a5e\x0e]\x0f\x10\x8fG\xf8\xdf\xff\xfdKJ\tr\xf1\xc5\x17s\xd5o\xafDJ\x93\xa4a\xee\xd5~\xed`\x81\xb9\x10\x1aEE\xc5\x14\x15\x15\xe3\xf7\xfb\xc8\xca\xca\xc40\x1c*%\x18\f\xfd\x03z\xc6I\x94\x06\x83\xc1\xbd\xc0\xdc)\x1e\x12\xfb\xa0J\xec\x14e\bN\x92\u007f\xed\u06b5\xff\x14\x95\xe5\x045&\x97^r6\xb7\xdcz+\x00[\n\xe6\xb3\xf4\xeb\xb7P\x14\x15Us\xfa\xa6j\x8a@W\x9c\x9e\xa7\u04b3Vt\xd5-.{\x92\xb2I\xb0%D#u\x84s\xbar\xd4i7\x92\xdfo$\x96\x11\xc34\f\xa7\x85\x9d\x10^\xf7\"\xa1\xea\xba\\\xb1b%_}5\xeb\u0799_|1\x11`\xd6W_\xc8{\xef\xfdC\xeb\xc2l\x05\xf3=\x8e\xe9\xb99)|\b\x87\xc3>\x00\u04f4\x88\u0162\xadw\xae\x85\x9f\xa6^{\xed\xf5\x942d\u0630\u00f9\xfb\xee\xbb\b\x873\x89\xc7\xe3?\x99R\xf8\xa5\u01ae]\xbb\xa8\xa8\xa8@UU6m\xda\xc4\xee\xdd\xe5l\u0672\x95\xd2\xd2\x12@\xfdQ\t\xec\xb6m\u06e8\xaf\xaf\xdf\v\xe8\x1d\xaaE6\xfbZ\xd32\u007f\x0f\xf4\xe3\xf18;v\xec\xf8\xa7\xaf=\x99t\xe4\x91w\xdc~\vc\x8f>\x11\x80\u0173_g\xcb\u06af\xf1\xf9\u04f0\u0756q\xaa\x10(\u00a1X$`#\xdd*\xd3\xc6k\xf3\x14.\x96m\x13\x8b6\x90\u04f9?\u01de};]{\rs\xbay\xe16\x96\x16\x8a#}\x14\x8a0-\x8b\x1f~X\xc1\xab\xaf\xbd\xfe\u0127\x9f}:\x02\xe0\xd5W^\x96\x0f=\xfcH\xebbh\x05\xf3\u0191\x93\x93\u3b46J\u02f2w\uae8ei\x9a\xb2\xb6\xb6\xae\xd9q\xbeu\xb4\x8c\u0474\xf0\u6957^\xc60\f22\xc2\\\u007f\xfdu\xf4\xeb7\x10\u00c8\xb7\xa8{\xe6\x81jmm\x1d\x91H\x14\u06f6\xa9\xact\x14Tk\u05ac\xe1\xddw\xdf\al\x02\x81@\xb3\x8aN\xc7d+H}}\rs\xe6|MUU\xf5>\xc1\xfc\xc7\xd4){&G\xe1\x9f;\xac8\x11z\x82\xcc\xcc,n\xbb\xf5v22r\x89\u0515\xb3\xe4\xeb7\x89\xd6W\xa2\xfb\x82\xce\xc6\xe2\xd4\xe6:\u007fW\x8af\u0562\x8e\x0eG`#\\\xf5\x8b\xc02M\"\r\xf5\xb4\xebv(\u01ddu\v\x1d\xbb\xf4\xc30\x12\xa9&\u04e2Io\u044a\xca*f\u035a\x93\xfd\xdcs/L\x9e:uj\x1f\x80?\xfe\xe1>~\u007f\xf7\u077c\xfc\xca\u02ed\v\xa3\x15\xcca\xc0\x80Av\x87\x0e\xed5!DUaa\xe1\xa2p8\x8ca\x18\xb2\xa6\xa6\u0199~-\x98f\xf18S\xaf+\x8d\xcf\xe7K=4MsU\x06\xe2\u07ca*\xf2\"\xeeW_}\x95\x95+W\x010~\xfcx.\xbc\xf0\x02\xa0\xe5Z/4m0\xd1(\u0143g\x9f}\x8e\x97_~\x19!4B\xa10\xba\xae\xa3\xeb\xbak\x8bk\xf2\xec\xb3\xcf3\u007f\xfe\xfc}\x82vS^|_\xf7\xb8\xa9\x14\u0463i\xfeY\xa5\xadC\xdbH\x8e?\xe1x.\xbc\xf8*\x00\n\x96\xcd`\xcb\u06b9h\xba\x0f\u02d68i&\x97\xcbW\x1a#t\xefO\xca&\x9b\x83m;t\x8biZD\"\xf5t\xea3\x82c\u03fc\x91\x9c\xdcN\x18F\x1c[\u068dQ=\xa0\ubeac\xad\xad\xe3\u06c5\x8bz\xbd\xf0\xe2K\x1f\xaf]\xbb\xba7\xc0\x13\x8f?\u0395W\\\u054a\xb2\xad`\xeeL\xf8\x11#F\x00\u0436m[\xdb+\x9f./\xaf\xc0\xb6M\xa7B\xad\x05\x02\x9a\xdf\xef\xc7\xe7\v\xe2\xf3\x05\xd1\xf5\x00RJ\xea\xeb\uba69\xa9\xa1\xa1\xa1\xc1Q#\xe8\x81\xd4s|>\u07ef\xfe\x94!\xa5DU},\\\xb8\x80\x8f>\x9a\x02@\xe7\u039d\x988\xf1*\x02\x814\x92\xc9x\x8b\u0778\x02\x81\x00>\x9f\xaf\xc9\xe6\xea\x00qmm-\xf7\xdcs/w\xdf}\x17\v\x17.\xa0\xaa\xaa\x8a\x9a\x9a\x1a\xbe\xff~\x11w\xddu\x0f\u007f\xfb\xdb\vMk!\xf6\xe1\x82(\x9b}\xdcWd\xee\xf7\xfb\xc9\xce\xce\xf9WW\n\x86\x11#=M\xe5\xa2\v/\xa5g\xaf!\u0636\u024ao\u07e7\xb6\xb2\x04\u055f\xe6F\u070d\xcfol9\xd7\u0737E\xba\x91\xba-A\xda`\x99&\r\xd1(]\xfa\x8fe\u0538\xab\t\xa5g\x92\x88G\x9a\x9c<\x04B(\xc2\xef\x0f\u0206\x86\b\xdf-\xfe~\xe0S\xff\xfd\xbfS\xa4\x94}\x00~\xff\xfb\xbbZ\xf9\xf3_\x8a\xe2l\xc9\x17\xb7sg\t\xf7\xde{\x1f\x00\xfd\xfb\xf7\xd7JJJ(**\x92\xa5\xa5\xa5\x98\xa6\xe3\xfb\xdcbvEEA\u05ddb&\u00c8\xb3z\xf5r\xbe\xff\xfe{6l\xd8\xc8\xee\u077b\x89D\"\x98\xa6\x85\xaek\x84\xc3a\xf2\xf2:1p`\u007f\x86\x0f\x1fN\u07fe}\u071f\x95$\x93\xf1_]1\x94\xa7\n\xb1m\x83\u0253\xdfa\xf3\xe6\xcd\x00\x9cs\xce\xd9\x1c{\xec1\x80\xd5\"_\x93\a\xa8YY\x99dddP[[\x8b\xa6\xa9)\u0149\xa2(TTT\xf2\xc4\x13O\xf2\xe1\x87\x1f\u04ff\u007f\u007f\x82\xc1\x00\x9b7oa\u02d6-\x18\x86\xe1\xf4\x1eu9\xf5\xa6\\\xf8\xfe@|\xcf\u0476m[\xbaw\xef\xf63\xdc\x03H$\x1a\x18:\xa4?\xe7\x9d\u007f%\x8f?z\x13[\n\u6ce5\xf0[\xfa\r\xff\x8d\xd3+\xd7J\xba\u0478H\xb5\x99s\xaeQ\xa6\xe8\x16\\\x80\xb6\x05(R`\xdb`\x1a\x06q\x04}\x87M \x11\xade\ue53f\x92\x8cG\xf1\x05B\xde\x1b\x89\x00\x11\f\x86dMM=\xb3g\xcf\x1dx\xdf\x1f\xfe\xf0\xb6\x94\xf2\f!D\u0663\x8f=\xae\xa4\xa5\x85\xec[n\xbe\xb9\x15q\xffS\xc1\xbcc\xc7N\\w\xdd5\x12 ##\xbc\u057b\u6492\x12\x99L&\x85\u05d2\xeb`\x03\x99\xd7V,\x99\x8c\xf1\xd5W\xb3\x98<\xf9\xef,_\xbe\x82\xb2\xb2\x9d\xd4\xd4\xd4\xee\x17Hrrr\xe8\xd4)\x8f\x11#\x86s\u99572f\xccQ\xf8|AW\u07db\xfc\xd5P0\x8e[\xa0\x8fo\xbe\x99\xcb\xf4\xe9\xd3\x00\xe8\u07bd\x1bW^y\x05\xa0b\x181Z\xf6<\u02e3}\xfb\xf6l\u0672\x856m21\x8c\x9a\x94V\xdc;1m\u06b4\x89M\x9b6\xb9^\xe7\x0eMf\x9a&\xa6i\xeds\x83h\n\xec{\x82\xba'S\x04h\u07fe\x1d\u077a\xfd<`\xee\x9c2l&L8\x9d/f~\xcc\x0f\u02fef\xed\xe2)t\xe97\x96P\xb8-V\xd4Db\xbb\x8d4\x1acq\xdbkL\xd4\u060c\u03b5\x10\xb0Ql\x81\xb0\xc10\x12(\"\xc0\xa0\xa3\u03a3\xben7\xdf}\xf16\xa6\x91@\xd7\xfd\xc8T\xb0/DZZ\x9a,.)\x95\x9f~\xfa\xf9\b\u02f4\xee\a\xae\xbb\xf7\x9e\xbbm\x80\x0f>\xfa\x88s\xcf>\xbb\x15u\xff\x13i\x16wQH\x80\xcc\xcc\u0302d\xd2\xd8\x04\xb0c\xc7\x0e\xdb\xe1\xcd\x0f\xae<\u0476mt]G\b\x95\u014b\x17q\xc5\x15Wr\xd9eW\xf0\xce;\xefRXXHMM-\xaa\xaa\xee\xa57\xf6\x16{EE\x05+W\xae\xe2\xe5\x97_\xe5\x82\v.\xe4\xfa\xeb\xafc\u035a\x95(\x8a\xeeF\xba-_O\xefE\xe5\xa6\x19g\xea\u0529l\u06f6\x1d\x80\v/\xbc\x90A\x83\x86\x00\xcdU\x1d--27\xcd\x04=z\xe4\u04f7o_l\xdbIv6-\xea\xf1\x80\xd8\xcbqx\xd2\xc2h4\x9aR\x92\xec\xeb=\xf9\xb1\xb9\xa9\xaa\x8d\n\x99\u07bd{\u04ebWO\xa44\u007f\x96\xe5l\xdbQ\x86\f\xe9\xc6Yg_\x8a\xaa\xeal-\xfc\x96\xe2\r\v\x9d\xe8Y\xd3\x1dO\x00!\x90BA\xba2C\xa1\xb8\x92\u0166f\\\x12\x90\"E\xb7H\x04\xc9d\x02\u0157\xc6\x11\xe3&r\xe8\x91\u3476\x85\u0133\th,E\xf2\xfb\xfc\xac+X\xcf\xf4\x193\xaf\xbd\xe1\u019b\x9e\xf2\xae\xee\u0733\xcfn\xb5\xe0\xf8O\x06\xf3\x13Np\u0295o\xba\xe9\x96h\xbbvmc\xe0\xb8\xce\x15\x15\x15\x1dT0\xf7\"r\u06f6y\xf9\u55f8\xe4\x92\xcb\xf8\xfb\xdf\u07e5\xb2\xb2\xb2\x19X\xec+\u0269\xaa\xaacW\xea\x96[\x03\x94\x94\x94\U000b7ffd\xc8\xc5\x17_\xc2G\x1f}\xe0r\xef\xbe\x16\x0f\xe8\x96e\xa1\xeb\x01\x96/_\xc1\xb4iN\a\xf7\xfc\xfc\xee\x9cw\u07b9\x00$\x93\x89\x16{\xedB\b,\xcb\xc2\xef\x0f1t\xe8P\xc2\xe10\xb1Xt/\xe9dSu\u029e:\xf1\xfd\x01\xb9\xe7w\xbe/:\xce{NFF\x06#G\x8e\xc4\xe7\v\xfel\xc9a\xa7:\xd4b\xfc\xb8\xf1\f\x1b~\f\xb6e\xb2a\xf9t\f#\x86\xa6\xfb\x11( \x14\x84\ua078\u04b8\x8e\x14\a\xd0e\x93\xe8\x1c\xc0\xb2%\xa6ec\xd96\x91\xfaz\xb4`\x16\xa3\u03f8\x8e\xfc~\xc3H\xc6\x1be\xc2\x12/w\xa2\n\xa1(r\xe3\xa6-,Y\xba\xec\xf6?\xfe\xf1\xfe?\xed\x19\u0334\x8e\xff@0\x1f=\xfaH\xe9N\x82\x02\x9fO\xdf\x10\n\x05\xa9\xa9\xa9\xb5\v\n\nS\x8b\xe3`\x8c@ \r\xd34y\xea\xa9\xff\xe1\xb6\xdbng\xe3\u018d\xfb\\\u0626i\xa6\x00\xd9+&\xf1>\x06\x83\x01\xfc~\u007fJ%\xa1\xaa\x1a\xabV\xad\xe6\x86\x1bn\xe4\x85\x17^\x04\x04>\x9f\xbfEG3\x8e\xae\xdcb\xee\u072f\u0678q\x13\x00\xa7\x9dv\x1a\xfd\xfb\xf7k\x06n-}\x1c\u007f\xfc\xb1\xf4\xe9\u04d7\xba\xba:TUiv\x0f\xffQ\xa4\xbd\u03c5\x95\xaaX\x96\xfb\x05\xf3\xee\u077bs\xe2\x89'\xa4Ny?O\x90!@&\xe9\u07ff\x1d\xa7\x9c\xea4^\u07b6~\x11\x15%\x85\bE\x03UE\xb8\u0560RU\x9c\xc2SE8\x0f\x0f\u061b\x81\xb3\xb7\x99\x81e\x83mY4\xd4\u0551\u046e\a\u01dc=\x89\xdc\x0e]\x88\xc7\"N\xc4\xdf\xe4\x87uM\x17I\u00d4\xeb\xd6\x15\xf0\xfd\x92\xa5wN\xfe\xfb\xe4\u06fck|\xfeo\u007fkE\xf4\xffD0\xef\u0631\x8b|\u5557\x04@\x9f>}k;w\xeeB\"\x91\xb0=\xe9\xdb\xc1(@\xf1\xfb\x9dD\xe73\xcf<\u01e3\x8f>FCC\xc3O\x8e\x9aTUu9W\a\xa8u]G\xd34TUC\xd75\xfc~?ee\xbb\xb8\xf7\xde?\xf0\xf6\u06d3QU\x1d\xbf\xbf\xe5\x02\xba\xdf\x1f\xa2\xb8\xb8\x98\xcf?w\xb8\xf2\xcc\xccL&L\x18\x87\xdf\x1f\"\x99\x8c\xfd*\x16\x81e%\xe9\xd7o '\x9exB\xaa\x9d\x9b\xdf\xef\xdf\xe7\xe6\xdc\xf4\xb4\xf5cN\x87^\xe9\xfe\x9e\xf7\xdf;\xcd\b\x01'\x9ex\x02\x03\a\x0e\xe0gO\x10K\x89\xdf'9\xef\xdc3\xe9?`(\xf1\x86j6,\x9f\x81P\x15\x14UsA[`\xab\x02\u02efb\xeb*R\x15HE \xf7B\x04\x99\x92\xb8XR:\xc9S)i\xa8\xaf\xa5c\x9f\xe1\x1cu\xfaU\x84B\xe9\xa9NE4\xb1\v\xf0\u9e88\xc5\xe2,]\xf6\x03\x9f|\xf2\xd9\xfd\x8b\x16-<\n\xe0\xfa\ubb93\x1f\xb8\x06c-y\xac_\xbf\xee_\xfc\xf9\x82V0\xdfs\xec\u0631C\x02t\xed\xdau\x99\xa2(\x12\xf0\x17\x16\x16\x12\x8d6\xfc\xe2/\xc1\x89D\x15>\xfdt\nO>\xf9$\xd1h\x94\xb4\xb4\xb4\xbd\x9ew\xf2\xc9'q\xc7\x1d\xb73q\xe2U\xa9\x04\x97\x97PSU\x15)!\x1a\x8d\x12\x8b\xc5\xdc^\x92V*r\v\x04\x02TUU\xf1\xf0\xc3\x0f3o\xde\\\x84\u041a\xf9\u0534\x98H\xc0\x8d\xc6V\xacX\xc9\x0f?,\a`\xec\u06238\xf4\xd0C\x01\xfbW\x13\x95{\xa0{\xc5\x15\x971d\xc8\x10b\xb18\xc1`\xe0'\x95\xf3\xefo\xd3\xdeW\xa4\xdd\xe8^\b\x03\a\x0e\xe4\xca+/\xc7I\x10\xff\xbc}l\x1d\x9e\xdbd@\xff\x8e\x9c~\xfa\x04\x00\xb6\xae[@\xbc\xa1\x12U\xd7A\x11)\x9e\xdb\xd6\x15\x8c\xa0\x82\xa9\v,\r\xa4\xe2x\xa7\xbbt:\u04a3]\\Su\xcb+*\xb2m\x1a\xa2\t\xfa\x8f9\x8da\u01df\x8em\x19\u0636\xebc\xd3D_\xafi\x1a\xd5\xd55|\xbbpQ\u01bb\xef}\xf0\xb6\x94r(\xc0\xb9\xe7\x9c\xd3\"\xe6\xc7\u018d\x85\xfb\xfd^\u07fe\x03\xfe\xa5\xdf\u0777o\xff\xbd\xbe\xb6e\xcb\xc6\x03\x87M\xbf\x86\xc56d\xc8`\x00\xce>\xfb\xcc\xd5\x1f~\xf8\xe1.\xa0CQQ1\x1b7n`\xf0\xe0\xc3\xfe\xa1\x17\xf4\xcf\u0271\xaa\xaa\x8f\xe2\xe2\xed\xfc\xf9\xcf\u007fa\xf7\xeerrrr\xc8\xcd\xcda\xfb\xf6b\f#\xc9\xd1G\x8f\xe5\x96[nf\u0528#\xc9\xce\xce&\x91HPPP\xc0\x9f\xff\xfc\x17\xfe\xfe\xf7wH&\x13dffb\x18&\xf5\xf5\xf5\xa9\xeb\xb6,\vEQ\xf0\xf9\xf4\x94V}\xf3\xe6-<\xf1\u0113\f\x192\x84\x8c\x8c,,\xabe\xd9\x18\xe8z\x80X\xac\x81\u0673g\x13\x89D\x00'\xdal\xdf>\x8fX\xac\xe1W\xa3\x9dw\xfc~\x92\xf4\xee\u074f\x9bo\xbe\x89k\xaf\xbd\xce\xf55w|Z\xf6\xe4\u021b\xea\u01fd\xc20E\x11n\xbb\xb8\x1f{\xbf4\f#Izz\x1a\x93&Mb\xc0\x80AHi\x1e\x90\xbc\x88\xe1Jw/\xba\xf0l^}\xe9\x19\u028b\v\u0639u\x19=\x0e\x1bO\xd2H\x80\x90\b\t\u00b2As\xa3r\x01\x12\x05M\u06a9\x86\x18 \x91\xb6\xa7F\x97\u062e\\Q\x95\x02#\x99\xc0\fe0\xea7WP\xb6\xbd\x90\xf5\u02d7\x12\b\xa55\xd9\t\x9c\nQM\xd3)/\xaf\xb4\xe6\xcc\xfd\xba\xdb\x1f\xfex\xff\x93R\xcaqB\b\xe3\x8c3~sP|\u043f\xfcr\xa68\xe9\xa4S$@\xef\xde\xfd\xfe\xd1\xdc\u040b\x8b\xb7\x1d\xbbl\xd9\x0f\x9d~\xf8\xe1\a\xa3\xacl\x97\x15\b\x042\xbbw\xefv\x88\xcf\xe7\u03c8D\"\xbb7l\u0638\xda\xef\xf7%{\xf4\xc8W\x87\f\x19\xa2\x0f\x1e|\xe8\xf6v\xed:\xce\xde\xd7\xef\xf3|\xf1\x9f~\xfa/b\xe8\xd0!r\xec\xd8c\xfe\xb3\xc0|\u0528Q\x00\xf4\xea\xd5o\xfe\xf8\xf1\xe36\x86\xc3\xe9\x1d\x8a\x8b\x8b\xf9\xee\xbb\u017f(\x98{\x94\u039bo\xbe\xc5\xf7\xdf\u007f\x0f@,\x16\xa3\xb8x\a\xe3\u019d\u00ad\xb7\xdeB\xbf~}\xc9\xcdm\x0f@2\x19E\xd7u\x86\x0e\x1d\xc6s\xcf=\x83a$y\xef\xbd\x0fH$\x92)u\u011e\u05a9\u0264\x81\xdf\xefD4\x86\x91d\u039c\xb9\xbc\xf7\xde{L\x9cx-\xba\xae\xff\xecQ\u073f:v\xef.g\u07bc\x0584Xo\x86\r\x1b\xf6\x0f#\u05d68L\xd3DUU.\xb8\xe0<\u05ad+\xe0\x89'\xfeD\xc1 \xe3A\x00\x00 \x00IDAT]]\xad[\xca\xefT\x88\xee\xebu)\x8aB8\x1c\xc64M\f\xa3\xa1\xd9s\x9a\xde_]\u05f1,gS\x988q\"\xff\xf5_\x17\xe1\xd4\x15$\x0f\xd8\x06\x050\xa0\u007f_F\x8d\x1e\u00e7\x9f}\xce\xf6\xc2\x05\xf4\x1dq:BU\xb0\xb1Q$\xf8\xa5\xc0\x92\x12Kq\x146R\xb3\xb1m\x81\xb0%\xb6\x02B*\bl\a\xe8\xdd\r\xc0\x96\xa0HP\x10D\x1b\x1a\xc8\xed\u0715\xa3\u03fd\x92]E\u06e8\xad\xac$\xe0\x16\xf7y\u01b9\xaa\xa2 \x84\xaal\u07bc\x95\x99_|yBfV\xe6\x8bR\xca+\x85\x10\xf2\xfd\x0f?P\xcf;\xe7\xdc\x03Z\x1a\xbcr\xe5\x0f\f\x1e|X\xea\xff\x1e\x90\xbb\xefS\xa0\xa4\xa48TXX8z\u0252%\xf9\xb5\xb5\xb5\x87\xf9\xfd\xfe\xa3\x8b\x8a\x8a\xa2\xf5\xf5\xf5\xf6\xc9'\x9f\xa8\x1b\x86\xd5.\x12i\bF\xa3\x11\x99L\x1a6\b_(\x14\xd4\x15E\xc54\r\xa2\xd1X\\Q\x84\xbd`\x81_|\xf4\xd1\x14%\x1cNo8\u7733\xcb{\xf5\xea\x95\x00\xb9\xb0c\u01ce\x9b\x86\x0e\x1d\xb2}\u0420C\xe6ge\xb5\xdd\rp\xf3\u0377\xc8=\xe9\xbb\xff\b0o\u05eec\xea\xf3^\xbdz}\xb3j\u056a\xa3\x8a\x8bw\xb0`\xc1\x02&N\xbc\xaa\x99\xd4\xeb\xc0\x82\xb9\x8f5kV\xf2\xd1G\x1f\x11\x8b9\x15\x8d\u0468\x13-\x8f\x1e=\x8a1c\u01ba \x1e\u00f6mW\xfa\xe64\xf7\xcd\xc8\xc8\xe2\xb6\xdbn\xe5\xeb\xaf\xe7\xb1k\xd7.4M\xdb\xe75\u06f6M\"\x91$\x10\b\x10\b\x04\x88Fc\xbc\xfe\xfa\x9b\x9cv\xdait\xe8\xd0\t!\xcc\x16\x01\x94\xde\u01b6l\xd926mr\x12\x9fc\u018cf\xf0\xe0CS\r\x1c~m#\x91H\xe0\xf7\x87\xb8\xfb\xee\xbb0M\x83\xe7\x9e{\x9ex<N(\x14\xc40\x8cf\x16\xb6M\xefW\xd3\x13\u05be\x005\x18\f\xa4\xb4\xf8\x97]v\x19\xf7\xde{7\xe9\xe9mH$\x0e\xecI\u02f6\r4=\xc0Y\xe7\x9c\u0367\x9f}\u038e\rKH\u0517\x12\nf\x93HDQ\xb0\x91B U\x89\xa5J\x92&\x18\x16\xd8:(\xa6DH\xe9D\xe7\xaa\xe2\x14\x18)\xce\u05fcf\u03f6\x04,I]]\x9c^G\x1c\u03c8\x93\x971\xeb\xdd\u05f1L\x13U\xd3\x1c\xcb\x00\xe1Q?R\b\x81\xbd~\xfd\x06\xe5\x9bo\xe6_\x91\x97\x97W\x06\xdcs\xde9\xe7Ze\xbb\xcaD\x87\xf6\x1d\xfe\xe5I\xbdn\xdd\x1a1o\xde|\xa5\xaa\xaaRy\u8847e<\x9e0\x81f@.\xa5l\xf7\xc2\v\u007f\u02c8D\"c\xb6o\xdf6\xf2\x9ak\xae>\xb6\xaa\xaa\xaa\xe7\xb6m\xdb\xd5\xda\xda\x1aa\xdb\x16\x8a\xa2\x92H$\xb0,\v\xc30\x89\xc7cn\x9eC\xb8\xef\xabt,\x9c\xa5\xed\xe6\xc0\xf4\x80s\x10\x91\x9eT\xd7\x1f\x0e\x87sV\xacX\x010\xd8\xe7\U000d16dbK\xf7\xee\xdd\xe3\xb7\xdf\xfe\xbb)\x1d;\xe6\xbd\u007f\xd2I\xc7\xfd0h\xd0\xd0\"o\u04ff\xef\xbe{\xb5+\xae\xb8\xdc\xee\u0673\xb7\xfdo\r\xe6M\xc7\t'\x1c\xff\u0357_~u\x13\x90\xb1j\xd5\x1a\u05ae]\u01e0A\x83\x81\x03\x1b\xb1z7r\xe6\xcc/(,,l\xa6J\x00\xa7$\x1c \x12\xa9C\u04f4\xbd\x8c\x944\r\xfa\xf5\xebO\x97.\x9d\u0675k\u05cf\xca\xdbl\xdb\xc60\f\xfc~\xc7\xf6\xb7\xb0p=_}5\x8bK.\xb9\xec\x17;\x85\xfc\xe3\u0701\x0eX,X\xf0m\xca5\xf0\xf0\xc3\x0f'\x18L\xffUQ,{\x03z\x94\xb4\xb4\f\xee\xbf\xff\x8f\xe4\xe4\xe4\xf0\u05ff>CII\t\xc1`\x10U\xf5\xa3\xaa*\xc9d2\xb5\x89\xefi\xc9\u073c\xef\xa7s\xc2J&\x93dggs\xed\xb5\xd7\xf0\xbb\xdf\xddF\x9b6\xd9\xc4\xe3\x91f\xf3\xea@\x9d6|>\x9d\xa1#G\x93\x99\x95\u016e\x1d\x85T\x94\xae\xa6\xef\xb0\t\xd4V\xc6\x01\x81%,\xa4\x10(\xd2F*N\xa7\n\xa9\bPAZn\x8b9\xd7\xfaV\n\u0464\xdc_\xa2I\x81*!\x19K\x12\xb5\xd2\x18>\xe1|6.[\u0136\x8d\x85(\x9a\xe6\xb0-\x12\xa4\xf0\\\xd4Ql[\xca\u014b\x97\x88\x8cp\xf8\x8e\x05\xdf~\xbbl\xcc\xe8\xd1\x1fuh\xdfA\xd6G\xea\t\xa7\x85\u007f\xf2k+,\\K\xbf~\x03\x91R\xf2\xc9'S\u0122E\x8b\x94\x01\x03\x0e\xb1\x00\uf0542\f\xa4O\x9b\xf6y\xc6\u018d\x1b\xf2KJJ\x87M\x9cx\u0571555\xc7l\u0738I)/\u07cdiZX\x96I<\x1eO\x15\x8a\x99\xa6)\x15E\x11N#\x10\x87B\u06f3VDU\xb5\x94$\u007f\u03e0,\x99L\xb2{w9R\u06ae\xd8Ae\u01ce\x12\u05af_\x1f\xf0\xfb\xfd\x17v\xeb\xd6\xed\u0082\x82\xb5e\xf7\xdc\xf3\xfbWF\x8d\x1a5g\xfc\xf8\xd3\x16\n!\x12\x8f<\xf2(\u007f\xfd\xeb_\u052b\xae\xfa\xad\x15\n\x85Y\xb7n5\x03\x06\f\xfa\xf7\x05\xf3\t\x13N\x9f\xfb\x97\xbf\xfcu]I\u024e#\n\v\v\x99?\u007f\x01\x83\x06\r>\xa0Me\xbd\u0098\xfa\xfa\x1a\x96.]F<\x9e@\u04f4f=\x1e\xcb\xcb+R\xc7\xee\xfdo\b\xec3\x91\xd9\x14\x04\xbc\xd7\xe0\x1d\xfb\x03\x81\x00\xd5\xd5\xd5\u031b7\x9fK.\xb9l\x9f\r\x84\x0f\xceP\xa8\xaf\xafa\u0672\x1fp\xb8\xc7\xde\f\x1e<\x98\u007f\x87\x11\x8fG\b\x06\u04f9\xe3\x8e\xdb\xe9\u07ff\x1f\xcf?\xff\x02\xf3\xe7\xcf'\x1a\x8d\x10\x0e\x87IK\v\xa56\\\xa7\xefg\xa3\u4c39\xbb\xa2#M\x1d3f\f\xd7\\s\r\xe7\x9f\u007f.\xaa\xea#\x91\x88\xfe2zk\xefd\x90\u04c1\x01\x87\x8fd\u1b19\xec,Y\xc9\u19ddOL\xd6b&-0\xc1\xb6l\xb0\x04~U\xa0\xa2:RD\xd5y-\xb6\x05\xb6pZ\xcba:\xe0.\x15\x1c\x8d\xba\"\x10\x8a\xc46-jj#du\xec\xc5\xf0\t\xe7\xb3\xf3\xf9'\xb0L\xc3\xdd\xf0\x1b\xbdn\x1c;^!b\xf1\x18\x8b\xbf_\xaa\xf5\xeb\xd7\xf7Y)\xe5:!DA8-\u0316\xad[\u945f\xff\x0f_\xd6\x17_L\xa7_\xbf\x81M\u05cb\xf4\x00|\xfb\xf6\xad\x87|\xfe\xf9\xe7\xc3W\xaf^\xdd\xfe\x9ak\xae>\xbc\xb2\xb2*'\x91\x88w6M\xb3\xf7\xb6m\xdb(//'\x99Lb\x9a\x16\xb6m\xb9\xf5\x03\x8dEa \xd04]\xec\x19\xc4\xed\xfd9?b\x94'S\xbeQ^m\x82\xd360\x81\xaa*\xd4\xd4\xd4RXX\xd8!//\xef\xbeM\x9b6\xdf\xfb\xf5\xd7\xdf|\xf0\xe1\x87\x1f\xbcu\xce9\xe7N\xbb\xe9\xa6[\xac\x9bn\xba\x85\x17_|\x9e\x8d\x1b7\xff{F\xe6\xf3\xe7\u007f\xc3QG\x1d\x8d\x10\u00ba\xe3\x8e\xdf}SP\xb0\ue23a\xbaz\x16.\xfc\x96+\xaf\xbc\x1c\x9f\xcfw\xc0J\xe0\x1d\x9f\x0e\x9d\x8d\x1b7\xa6|G\x9aj\u01e5\x94\xacZ\xb5\x8a\xda\xda*\u06b4\xc9$\x1e\x8f\xeds\x12\x94\x95\xed\xa2\xbc|\xf7~\x8f\xe3M\x9f\ub045\a\xfe\xabW\xaf\xa6\xb4t\ayyy?\x1a\xd5\xff\x12\u00fb\xc6m\u06f6\xa5\xbc\xb8{\xf6\xecI\u07fe}\xd8_g\x9e_\xd3\x10B\x90H8\xf9\x8e\t\x13\xce`\xe8\xd0\xc3\xf8\xec\xb3\u03d86m:\xf3\xe7/\xa0\xb2\xb2\x12EQ\xdd\xc8L\xa4\xf2}M;`\x85BA\x86\f\x19\u00b8q\xa7r\xfe\xf9\xe7\u04eb\x97\xf3\xde\x1chje\xcf\xe0\xc1\xc6\xc6\x17J\xa3\xff\xd0\xe1,\x9c5\x93\x92\xad\xab\x88\x8bJ\u04bbeR_[\x0f1\x05b&\"ia\x1b\x02M\xd8H\xdbi\xe7\xa7\t\x05K\xb1\x1d\x8d\xb9\x05\xa6%\x91\xb6C\xb1\b\u06e1X,\x17\xbc\xccx\x82\xb8\x11\xa0\xe7\x11\xc7\xd3g\xf17\xacY8\u05c9^\x9b\x18\x05\b!R\r.**\u02993\xf7\xeb\x0e}\xfa\xf4\xb9\t\xb8\x0e`\u02d6\xad\xec\xdcUF\xc7\xf6\x1d\xf6z-\xc5\xc5\u06d86m:\xd7^{='\x9f<n\xcf\u0377\u05d3O>9~\u04e6-G^{\xedu\x03l\xdb\x1eTVVFYY\x19\x89D\xd2\xcdg\x18)\x16\xbf\xf9zSR\xc0\xdc|~;\xf7\u054b\u029dG\xa3\x0e\xdfk\x8a-]1~c\x91\x95\xe7&\xe99\x9b\x89\xd4)\xad\xe9\x89\u0272L6o\xde\u0136m\xdbD\u01ce\x1d\xcf+,,\xfc\xcd\xef~w\xdb\xfb\xe7\x9cs\u059bG\x1e9\xe6\xabk\xae\xb9\x1e\x80%K\x163|\xf8\xc8\u007f/0\xef\u0631C\x13\xaa\xe5\xb8\x17\xbe\xf9f\u078d\xbbw\xef\x0e\u035f\xff-K\x96,\u1a23\x8eA\xca\xc4\x01\x05\x92\xa2\xa2bJKK\xf7\x99\b\x9b;\xf7k\xbe\xfbn1'\x9f|*\x81@\x90d2\x912k\xf2\f\xb8>\xfex\n;w\x96\xb9\xaa\x06s\xbf\x1bGS@\xf7\x80\xbb\xb4\xb4\x94M\x9b6\x92\x97\xd7\xf9\xe0\xc7\xe4\xee\xe9c\xf5\xea5)\xd7\xc0\u07bd{\x92\x9b\xdb\x1e\u00c8\xff\xdbT\xf99\x00`\u0429S\x17\xae\xbd\xf6z&L\x18\u03d2%KY\xb1b\x05\x05\x05\x85\x14\x15\x15S]]\x8da8ADFF\x06\u077au\xa3_\xbf~\f\x192\x98\xa1C\x87\u0437o?@!\x91\x88\x1e\xd0\xd3\xe3\xfe\x02s\xdb4\xc8\f\xf9\xe9;\xe0\x10\x00vo\xddH\xd5\xce\":\x1d2\x94x2\x06\x9a@\xf8\x05VRAIZ\x88\x84\x8dL\x9aH\xc3FJ\a\xc4l/\x02\x176\xb6\x05\xd8\x12i:\x85D\xd8\x02Ew\x14.\xb1h\x946Y\xed\x18v\xea9\x14\x17\xac\xa2\xa1\xbe\x1e\xdd\x1f\xf0\x10/\x05t\xb6\x9bP.,\\\xcf\xd7\xdf\u033bDJ\xf9\x8a\x10b\xd9\t\xc7\x1f\xc7\xd6\xed\xdb\xf7z\x1d_~9\x93.]\xba\xef\xb9N\xd4Y\xb3\xbe8g\xfa\xf4\x99'\x9fq\xc6Y\xa3\xaa\xaa*\xfbVVV\xb2k\xd7n\xa2\xd1(\x8a\xa2\u062a\xaa\n!R\xddNS`\xbb\xaf\xc0\u010b\u031d\x8f\x8eE\xb0\xd7(\xd5m\x85\x8d\xf4\xac\u007f\xbd\xc6\x1e^\x03\x12ic\xe36\xf7p\xbf\xef\xd9\r\xe3Ya\xa7TOJ\x93\xa8\x1d,\u02d2EEE\xb2\xa4\xa4T\u07fau\xeb\xc5\xc5\xc5\xc5g=\xfb\xec3\xafM\x9at\u00fdB\x88\xda\xe1\xc3G\xf2\xd4S\u007f\x12w\xdcq\x97\xfc\xb7\x01\xf3;\xef\xfc}\xea\xf3\x93N:\xb5\xcd\xdbo\xff]]\xbdz\rEEE\xbc\xf9\xe6[\f\x1e<\x98p8L\"q\xe0\x00\xbd\xb2\xb2\x8a\xda\xdaZ\x14E\xa4\xfcF<P\xaf\xae\xae\xe6\xb1\u01de\xa0S\xa7<\x0e9d0{\x9a\x80}\xf6\xd9'\xbc\xfe\xfa\x1bX\x96E \x10H)\x1f\xf6G\xb7\xec\x19\xb5\xd7\xd4\u0532cGI\x8b\xb8\x17M\xc1\xbc\xa6\xa6\x86@\xc0O\xff\xfe\xfd\xf7\xb9\xc9\xfd;\x8cX\u0331-\xee\u0739+\x9d;wc\u0084qTUUSWWO,\x16sU*\x82@ @\x9b6\x19dee\xb9\x9e\xe7\x0e\a\xef\xd5\x17\xfc\u049b\x9c\x04\xa4m\xd1\x06\x18\u0437\x17\x999\xb9\xd4W\x95\x13\xad*G\xd7AE`\t\xa7x\b\x9f\x00]E\x04\x05\x18\x02\x12\x16\xc4,DR\xa0Z6\x8a\x00[*$m\xdbQ\xb3\xe0x\xb6\xa4\u0339\xe26\t\x9fI\x9d\x9a\xa4\u00e0\x91\f\x18}<\x8b?\u007f\x1f\xfc\x01/\x94\xf5\f\x19\u075f\x17\xd4\xd7\u05f3t\u9cb4\x0f?\x9er;p!@~\x13\u04f1-[6\x88\x1e=\xfa\u0213N:\xa5\xe9z\xe8\xf8\xfc\xf3\u03dez\xf9\xe5\x97]\xbfe\xcb\xd6\xc1\xa5\xa5\xa5Zee\xa5\x97\xb714MS}>\x9f\x02({6\xd4\xf6\xd6X\xf3\xfb RHo#\xb1M{\xaf\xf5g[\x0e%c\xfd\x9f<\x86\x9a\xffm\xab\xc9\xdaQT\x15MU\xbdz\x13\xe1\xf0\xf3\xd2\u07bau\x9b]^^\x11,))\x99TYY1DJy\xb9\x10b\xd3\x1dw\xdc%O;\xed4\xf1\xfc\xf3\xcf\xc8=7\xb5_\x1d\x98O\x9d\xfa1g\x9ey\x16\x00\xf5\xf55C\xee\xbf\xff\xfe\x0f\u05ef_\xef\x8f\xc7\xe3\x00\xbc\xfd\xf6d\xbav\xed\u01ad\xb7\xdeL \x108`\x9cr\"\x11wmO\x95\xd4\xed\xf1\xa2-)%\xf3\xe6\xcd\xe3\x82\v.\xe2\xea\xab'2z\xf4h\xc2\xe10\xe5\xe5\xe5\u03181\x83\xb7\xde\xfa\u007f\xec\u0739\x93`0\xf8\xa34\xc9\xfe\x12\x9c\xb1X\x8c\xda\xda\xda\x16\x03\u6595\xa4\xb0\xd0)\xb8\xc8\xcb\xcbc\xc0\x80\xfe\xfc\xbb\x0eo\xf3J$\x1c\xfa\xcc\xe7\xf3\u047e}\x1e\xed\xdb\xef\xef'l\xa7\x89\x83;\x0f\x0ff2\u061bJ\x83\xf2\xf38\xe6\xb8\xe3\xf9t\xea\x14j\xcaw\x82\x04U\x15(\x96\x03\u0336G\x1d(\x02\xe1S\x10\xba@\x06U'R\x8f\x99\u0204\x85\x86\x8de\xb9d\x85l\xf4b\x91\xa6D\x899\x96\x00I#F\xa8]\x16\x87\x9et6\xdb\xd7.\xa7\xacx\x1b\xbe@\b)\xad&\xe0\x99\xf2\xba\x91\u06f7o\x17\xb3f\xcd>#\x1a\x8f\x8d\v\x05\x82\u04fd5\xf5\xd8c\x8f\xea=z\xf41\xdc\xff\xa7\xbf\xf7\u07bb}\u05ef/\xbc\xf8\xa2\x8b.\xbcp\xed\xda5\x1d***\xa9\xac\xac\xf6\xfa\xb5J\xc5\xe11\xf4=\xd7~s\x8a\u0109\x99m)\x9b\xb9FJ\x01\xd86\xb6e\xa5(\xa6f \xa9\xfb\xd0C!B\x81\x10z \x88\x1e\f\xe1\x0f\xa5\xe3\v\xa5\xe1\xf3\a\x91\xaa\xcf\u9deai(B`\x18\t\x92\xb1(\u0246:\x12\xf55\xc4\x1bj\x89\xd5V\x93\x886\xa4\x92\xac\u0496\xae0Bs\xe7\x87T\x84\x10JCC\xbd\\\xb9r\xa5hhh\x18\xbdk\u05eeO?\xff\xfc\u04f3'L8\xbd\xe0\xb3\xcf>\x93\x17_|\xe1\x8f\xea\xf2\u007f\x15`\xee\x019\xc0_\xff\xfa\xccog\u0318\xd9s\xe5\xcaU(\x8a\"-\xcb\x12\xc9d\x92\xb7\xdez\x9bc\x8e\x19\xcb\u0631cS2\xb2\x03\xc3\x15\x8b}\x82\xae\x97\x10]\xbbv\x1dw\xdf}/m\u06f6\xc5\xef\xf7\x13\x89D\x9a\xa9W\"\x91H\xb3\xe3\xf6\xfe\"q\x0f\x04\xfc~\u007fJ9\xd1r\xe8\v\x85\xf2\xf22v\xedr\xf8\xff\xdc\xdc\\\xb7\xcaU\xfe\u06fb\xe2I)I$\x12\xbf\x9e\xebu\xe3\xc2v\x99\xe9\f\xe8\u06db\xa9\x96EEi\x11\x89H\x12MU1\x15\v\u06e5\x01\xa4t\x13\x9d\xd2%\xdc\x15\x10A\x05|:2\xa1 \xa2&:N\xa4\xeaq\u0136\xed\x16|J\x89L\xd8`@\u012a\xa5}\xf7\xc1\x1c2v\x1c\x15\uff88\xb4\xf7P\xfb8\xa5\xa5(B\x88\x9a\x9aZ\x96,Y\x1a|\xe7\x9d\xf7\xae\x91R\xce\x12B$\xcf8\xf3L\xed\xd3O>1\x00\xdeyg\xf2\xb8\a\x1f\xfc\xe3\xbd?\xfc\xb0r\u052aU+ihh\xa0\xa1!B2\u9038\xa6iBJ)\xf6\x06p\x1c{\x01o\x9d\xb9fa\xa9^\xacV\xf3\x80J\xf3\xf9\b\xb6\xc9\xc1\x9f\x1e\u019f\x96\x8e?-\x03\u007fZ\x06\xa1\xac\xb6\xb4i\u05d1\xb4\xb6y\xa4\xe7\xb6'\xb3m\x1e\xa1\xac\\|\xe1L\xfc\xc1\x90\x03\u07b8.\x94\xb8'\x16\xe9P\xa4\xb6\x91 QWE}Y1\xe5[\n)[\xb7\x94\xe25K\u067d\xb9\x00\xcb2\xb1,'I\xae\xeb\x1a\x9a\xa6\xba\x1b\x8c\x10\xb6m\u02cd\x1b7\x8a\xfa\xfa\xfa\xfe55\xb5\xd3\x16,\x98w\xe7\x981c?\xbc\xe0\x82\x8b\xe4s\xcf=\xc7\xf5\xd7_\xbfO,h\xf1`\xbej\xd5r\xc2\xe1\f\xf2\xf3{\"\xa5\f\x9dp\xc2\xf1\xa3\v\n\nH&\x93R\xd34\xe1M\x90\u035b7\xb3f\xcdZ\x8e:\xea\xa8\x03\xa6;\x0f\x06\x83\xf8\xfd\xfe\xfdz\xb1\xf8|>\f\xc3 \x1a\x8d\xb2}\x1f\u071f\u01c3\xef\xc9\xd7\xed\v\xc8\xc1\xd1rgff\x12\x8dFS\u0296\x83=\xbc\rm\xe7\xce2<\x1b\xe2\xdc\xdc\\:t\xe8\xe0\xc6w\xad\xa3\xa5\r\x87\xda\v\u0465Kg\x84\xa2PWVJ\xb4\xbe\x16\xdd\x1fBS\xc0R@X^\xcc,\x1b9\x11w:\nU \x82*\xaa.@\x15(\xa6D&m,KbI\xe7\xae+\xe0\xfa\b\b\xac\xa8A\xb4\u06a4\xef\u19f2q\xe9\xb7l+\xf8\xc1md!R\xc9C\xcfS]QT\xb9q\xc3F1o\u07bcqg\x9c>\xe1:\xe0\xe9O?\xf9\xc4L\xc4c\xdd\xee\xbe\xe7\xee{\xdf{\uff49\u02d6-\xa7\xba\xba\x8aD\"\xd9\xcc\x12\x03\x10{\xaa\xc0\x1a\xbf\xe7\xca(]%\x89m[\u0626\x99\nk\x03\xe9\x19du\xeaNF\x87.\x84\xdbw&\xabcW\xdat\xe8L0\xbb\x1d\xe1\x9cv\xa4g\xb5\u00d7\x9e\x81\x16LCSU\x04`z\u007f\v0l\xe7/\b\x01\x9a\x04\xd3v\xde7E\x80\x94\nR\x82?\x10\"\xd8&\x9b\x9cn\xbd\xe93\xfa84\x01\xbb7\xae\xa5\xf0\x9b\xe9\xac\xfej*\xdbV,\u00b2LG!$mt\xdd\xe76;q\xf6\xbd\x9d;w\x8aE\x8b\xbe\xcb\x0f\x85B\x1f|\xf9\xe5\u0309'\x9dt\xca+\x93&Mb\u04a4I\xbc\xf9\xe6\xeb\\v\xd9\x15\xbf.0?\xf4\u0421\xfc\xfd\xef\xffO\x00\xf2\xf5\xd7_;\xa3\xa4\xa4d\x80\xdbzM\xec\t\xd8k\u05ee\xc30\f\x97\xaf\xfe\xf9u\xe7m\xdb\u6493\x93M}}\xfd^\xfd\x1dM\xd3l:\u0270m\x99\xf2[\xd9W\xc2\xe5\xa7\xf0\xcbR\xcaT\xc5gzz:\xb9\xb9\xb9-\u6f94\x96\x96R[\xeb\x80y\x87\x0e\x1d\b\x04\xd2~&O\xee\xd6q \xc0\\U}\xe4\xb6k\x8f\xee\x0f\x10\xad\xa9$\x19i@\xf7\x87\x1c@\x15\x8d\x8a\x1c\xe1\xa4\xfaR`\xeeQ*\x02\x89\xa2*\u0220\x8a\x1dT]\xfd\xb8\xc42m,)Q\xa4\x13\xdd#\xc0\xb6\x05\xf1\xfa\b\xe9m\xba2p\xe48vm\xdf@<\x1eE\xf7;y$\xe1\x05\xd2B\"TE\xc4\xe2q\xeb\x87\xe5+\xb4\xe93\xbe\xb8PJ\xb9\xe0\xaf\xcf<\x93\u007f\xe9e\x97=\xb9l\xd9\x0f\xf9\u06f7ow\x03 \x81\xa2\x88f\x94US\u00f3\x94\x9d\xb4P\x90H\u01f6\xd70\xb0\f#\xb5\x0eC\x19Y\xe4\r<\x9c\x8e\xfd\x87\u04b6\xd7@\xb2\xbb\xf6!\xbbs>\xc1\xecvh>\x05lG\xb1\xa3\xba4P\u04b21L\x03\xd3H\xa2\xe2\x18\x8dY\xb6\xd3\x1a\u06e3\xd6U\xc5q\x93\x94\x80\"$6`\xd9\xc2\xf5\xa5\x01UU\x1c\x17N\xa9`\xab*y\xfd\x06\xd2e\xe0@\x06\x9fr.\xdf}\xf0\n\x8b\xde{\x89\x86\xaarL\x04B\x98\xf8\xfd>\x84\xb0\xb1m[\xa8\xaa\u02ae]e|\xf5\xd5,TU}y\xc1\x82o\xca\u018c9\xfa\xf3;\xef\xbcC\\v\xd9\x15\xf2W\x17\x99\x1bF\f]\x0fJ\x80\x0f>\xf8\xe0\xf8\xd2\u049d~\x97\xb0V\xf7\x04\xf3/\xbf\xfc\x92\x82\x82\x02\x06\x0f>\xacY\x92\xf2\xe7\x89F\xa1s\xe7.\xe4\xe5ud\u06f6\xed.ol\xed\x15u7\xe5\xd0\xf7\u03c3\xcbf\x1e\x1e\xfb\x02x\u03ec\xa9\xba\xba\xca\xddH\xda\u04af_\xbf\x16s_\xca\xca\xcaR\x1c~^^\xdeO\u069cZ\xc7\xc1%\xceC\x19Y\xa4\x87\xdb\xd0P]I\"\xda@ZN{$\x12U\x11h\x9a\x02\x86t\xacl\xed\xa6\x80\xeeT\x89\xba\x04\x82\u04c2BU\x90>W\x9f(\x14\xa4%\xb1\\6W\xd8N\xa8.5I2\x91\xa0\u03f0\x93\u067af\x11k\xbf\xff\x12[\xf77\x82\xb1\x10N\x85\xa9\x94\xf8\x03\x01e\xe3\x86M\u031e=wP]m\xed\x9b\xef\xbe\xfb\xde\xc0\x82u\uba29\xa9F\xd34\xe9IR\x9a\xae\r'pRP]\xc7Q\x89\xc0\x966\xa6i\x91\x8c\u01f1M\x03\t\xa8\x9aN^\xdfC\xc9\x1fq,]\x0f\x1bC\xdb\u0783\xc8\ua50f/\xa0\x12\x8bK\xa4i`$\xe3\x18qGo.\xdc\xfc/\xd2=uH\xf0\xb9o\x8d)\xdd\xcaW\xf7-U\x04\xa8R\xa2\xbaI]7\xbcCU\x15\xfc\x9a@W\\\xba\a\x89\xb4-,\xdb\"n\n\x14\xa0]~w\xc6\xdf\xf6\b\xed{\xf6g\xe6\xd3\u007f\xa4\xa2x\v\x00\xba\xa6\xa3\xfb|\x18F\xe3)\xa4\xa2\xa2\u009c3g\x8e\x16\b\xf8\xff\xec\xea\xf2\xb7\xbc\xf1\xc6k\xea\xe5\x97_i\xfd\xaa\xc0\xfc\x99g\x9eS\x00{\u04e6\xc2\xfe\xe7\x9e{\xc1\xa8\xba\xba:\xf7\xbe\xee\r\x1c\x9b6m\xe6\xb9\x17^\xe6\u017f=\x87\xae\aI&\xa2?\x9b\x8b\x8fm\x1b\xf4\xec\u0643\xee\xdd{\xb0p\xe1w?\xda\xe7\xf1\xc7<\xb0\xf7l^\xe0%D\xf6\x04\xfc=\vP\xbaw\xef\xf63v\xa4\xf9\xd7GEE%\x91H\x14\xbf\xdfO\u06f6\xb9\xad`\xfe+\x18\xa1p\x069\xed\xda\xd3P]I2\x16I\u9f85\x04MQ\xb0UW#-,\a\xbaE\x93~\xa1\x02W\xaa\xe8&L]\xac\xb7\x14\a\x98\xa5\xe5&B\xa5\x1b\xa1\x9a\x82D<Iz8\x8b\xc1c\u03e6x\xe3\n\xeak+\xd0\xfdi\x8dk\xa71\xb6\x11\x80\x9c5kv\xe8\xeb\xb9s\x06\x16\x15\x15\xa1(\xc2\xd24]\x95\xd2nV\xc0\xe3\x9d~UUs\n\x97\x10\x18\x96\x8d\x91L\x92\x8c\xc7\\zR#\xdc.\x8f\xae\x87\x8d\xa1\xf7\x98S\xc8\x1bx8Y\x9d{\x10\xcaH'\x123I\xc4c$\xa2N\xe5+R\xa4\xac\xd8=\xce\u06f6\xc1\x96\x8e\x15\x81-!f:\xad\xf5lo3K)r\x1cfQW\x04\x9a\"A(h\xaa@\xf5\uc445\xe7\a\xef\xa6 D\xa3F=Z\x1fE\xf7\x058\xe2\u070b\ted\xf2\xf1#7SQ\xb4\x99\xa4\x91D\xf7\xe9\xf8|>\x12\tG\u07aci\x9a\xb6s\xe7Nk\xf1\xe2%\xbd\x1fz\u807b\x80k.\xbf\xfcJ\xeb\xbb\xef\x16*G\x1c1\xcan\xccd\xb5\xf0Q]]\xad8\x89\x90\xf7G\x00\xfd] \u072f\x89\xf9\xbb\xef}\xc0\x1b\x1fNs9l\x95\x9f#g\xe8y\xac\xa4\xa7\xb7a\xf8\xf0\xc3\t\x87\xd3\xd9W\xd6|\xcf\xe3\xdf\xdeI\x99\u01afk\x9aJ \xe0\xdfo\x13\x83=\xc7q\xc7\x1d\v\xa8?[G\x9a\u007f\xf5\x94\xe2\x9d\x18\x02\x81\x00\xd9\xd9\u066d`\xfe+\x18\x19\xe9id\xe7d\x13\x8f4`\xc4b\xa9\xee\x13\xb6\xab\x8f\x16x\xa0\u04f4\xd0\xc7\x13i7\u2ba7\x1a7%\u0616t8\a[b[\xce\x03\v\xa7\xb24i\x13\x8f\xc7\xe9\xd4g\x04\x03\x86\x9f\x04\x12,\xd3H!8M\xdc'\x15U\x15\xbbv\xed\x92E\xc5;,\u007f UUU\xa5\xb4\xf7\n~\x9c\xa4\xa6\x82iY$\x92\x06\u0446:\"u\xb5$\xe31\x82\xe16t\x194\x82\xe3&\xdd\xcf\xf9\xff\xfb\x01\xe3\xee}\x96\xc1g\\F\xdb\u0783\x90\b\xea\xabj0\xa2\x11t\xe1PF\xaapZ\xe8y\ax[:\\x\xd2r\x94\x99q\x13\x92\x96$fB\xdcr^\xa6)%\x86\xed\xbeg\xd2\x01y\x84@SU\xfc\x9a\x82\xae:\xe5\xff4\xed\xbe\x97z\xfb\x9a`\x84\xa2`$\xe3$\xa3\x16\x83O\x19\xcf\xe9w\xfc\x89\xb4\xcc\x1c\x12\x89\x04\xa6i\x11\b\x04S\x9e\xfa\xae\xeaE\u0670a\x03\xeb\xd6\x15\\=m\u06a7\xe3\x01\xde}\xf7]\xf5W\x13\x99K)\xc9\xce\u0392\x00\x05\x05\x05=\u0768\xdc\xdc\xf3\xba}\x814\xba\xf4\x1dJy\xf1F\xea\xaav\xf1\xe4\x13\xffM\x87\xae\x87r\u0310nhD\u070d\xff_\xbf\x16\x80\xf1\xe3\xc7\xf1\xe1\x87\x1f\xf1\xed\xb7\v\u007f2\xf0y\u050b\xa3+u\"n\xa7\x11\xf0O\xe3\xf5{\xf5\xea\x95j\xc3\xd6R\xda\xc8y\x96\xb7~\xbf\x8f6m\u06b4\"eK^G\x0e$\x90\x95\x1e '3\x83u\x898f2\xe9\x02\x92K\r\x02\xaap|Yl\x84\xc3k\x8b\xa6 \u07ac\xa0\x11\xcbv\xc0\\\x91\xa0\x9a\xeeF\xe0VW\xdaB\xba\xa9T\x81\x11K\xe2\xcbl\u00e1c\u03e1h\xc32\xcavlD\xa2\xa7:\x86\xa6*)m\x89\xaaiB\xd3T\xd59\xd9:rB\xa7\x01\xb5H\x81\xa1\xed6\xc70\xe21\xa4\x94\x84\xdad\x91\x99\u05ddN\x83F\xd0k\u0329t\x188\x9c\xb4\xdc\x0e\xe8>\x05\u06c4d<\x86i\x99h\x02\x14UC\xb8{\x8d\a\u0216\x14\xa9\x86\x1b6{tW\xf2\xf6\x1c/\xf7@\xa3$S\xc1\x89\xb4}\x9a\x82\xae)(\n\xa8B\xa04\xba\xffz\xf9`\x8fU\x02!S\xf4\x8cpv(,\xcb\xc02T\x86\x9dy6\xb5\xbbJ\xf8\xec\u007f\xee!\x1a\x8d\xd0&\xb3\r\xd9\xd99TVVx\xa6_\xc24-\xb9j\xd5*\xb1d\u0272{\xa5\x94\x8b\x85\x10\x15R\u06a9\"\xa4\x16\x1d\x99\u007f\xfa\xe9\x14\xa5\xba\xba\u0192R\x86jjj\xfa\xef\u06b5\x1b!\x84\xb2g\x04\x98\u06f97\u01dcw7\xdd\x06\x8c\x06\xa0p\xd9<\x9e\xfe\xf3\xb3,^\x1f'\x12W\x11?\x13\xd9\xe2y^\x9fv\u0684\xbd\x1aR\xecI\x914}4MDy\x91\xb5cw\x9blf\u04b4\xbfq\xe1\x85\xe7\u04ed[\x0f\xa4<\xf8\x8e\x89\xceuJb\xb1\x98{\xc2\u041a\xa8lZ#\xf3\x96\x1c\x18\xa5\xf9u2BA\f\xd3\xc42\x12\x0e\x9f\x90j\v'Q\\5Fc\u0562\xdb\xfc9\u0144\b\xb7\u0273\xc0\xb4\x1d\xf0p\u0336\x04\xc2rM\xba\xa4\x9b\x10t\x1bA[\x96E<\x16!\xb3c>\x83\u01dc\x89\xae\xfb\x91\xaeX=\x15d\xb9\n)e\x8f\xc6\x16B\xa8(Bi\x8c\x82m\x9bd<\x8a\x11\x8f\x92\u06ed7\x87\x8c\xbb\x80\xa3o\xf9\x13g>\xf5>\xc7\xfd\xfeY\xba\x1e}\x06zN\x1e\r\t\x93\u02aa\x06j\xea\x1a\x88&-\x926\xc4m\x88\x9a\x92\xa8%\xa9KJ\"\x06DM\a\xd4\rK\x12\xb7\x9c\xa8\u0734\x1b#pO\xa9#\xf6\x00h[\x82\xa6\b\xd2|*A\u0761V\xbc\u04cc\xe2n\x8a\xba\x02~\x15|\xaa@\xf5z\xab\xcaF\xa32p\xe8\x18\x80D,\x81\xb4a\xf4\x05\x13\x19~\xfaE\x98\xa6IMM-\xa1p\x06\x99Y\xd9)\xdf\x1f!\x84\u0639\xb3\x8c\x82\x82\x82#\xdfyg\xf2a\xce\xfb\xa4\xfc:\"\xf3\xe5\xcbW*\x80=o\xde\xdc\x1e\xb1Xl\x90k;\xbb\x97\x8a%\xaf\xe7at\xedw4\u0246\x04E\x05\x8b\xa9\xad,a\xfe\x8c\xb7\xe8\xdas(\x81K/\xe0\x90Nq\xfc>\xf8W\xf3\xa1\x8e\xf9\x95\x8fK/\xbd\x94\xb9s\xbf\xe1\x8b/\xbe\xd8o\x04\xbf'\x00\xee\xcb\xe3\xfa\xa7P\x13\xf9\xf9\xf9L\x9cx\x15\xc0A\xf72W\x00MuZ\x8ey\x8bN\xd5tTM\a\x1c\x1d/\x12Z{\xf6\xb6H4O\xf1\u0376ma$\x13H\x17\xccE\x13\xe5\x8a\xf7\u007f\xa9\xb8\x06Y\xd2u\au\x13|R8`g\xb8\xe0+\\\x13-'d\x95\xa9\xed\xdcvs\xa9H\x81\x15Obj\x1a\xf9\x83\x8f\xa1\xeb\xcayl.\xf8\u07b1\xc8udx.\xed\xe0l\x14\x9ew\x8b\xf7\x90\xb6DZ\x166\xa0\xfb|t\x1c|$\x9d\x87\x1dC\x8fQ'\xd3a\xe0p\xec@\b\u06f4\x89\x18&v<\x8a\xb4\xadF{^\x9a\xf2\xd4\xce?V\x93\x90Cu\xbfg7\tElI\xea5y*\x1eM\b\xcc&\xbf\u03ef)\xa4\xfb\x14|\xeeRHu\xd6\x13\x12\v\xc7\xda\xc0k\xab\x8at*m=\x9e\xddF\xa04v\xe2\u00d3P\xc6\x1a\x12\x842\x82\x1c}\xe9Ml_\xb5\x84\x1d\x05+\xa8\xaa\xa9\xa1mN\x0e\xd1H\x84\x86\x86:\xc0Q\u022d\\\xb9\x8a\xbe}\xfb\x9c%\xa5\x9c#\x840\x17-Z\xc0\x91G\x8ei\u0651yeE\x85\x020o\u0782n\x96e\xf5\xb5\x9cb\x85f\t\x11\xdd\x1f\xa0]\xb7CAjt\xe9=\x8a!G_\x8a\xa6\xfb\x89\xd6\xedf\xda;\u007ff\u019c%\x945\x04\x11H\u051f\xe1\u055af\x82\x8e\x1d;q\xdbm\xb7\u042d[\xd7\x1f\x8d`\xf7\x17m\xef\xeb{\xfb\x03\xf5\a\x1f\xbc\x9f.]\xbac\xdb\xc6A\x8b\xca\x15G^\x8c)\x14j\x92\xb0`\xe9\x0fl\u073c\u074d\xccu\x84\x10\x18\x86\x81\x914S\xfe\xed\xad\xa3\x05R-B \x14\x05i\xdbX\xa6\xe7\x18\xe8\x16\xd2\xe0&\u294dg'%\x84\x82PDc\xbb;\xe1D\xe5\x86\xddX\\$\xbc\x877\xb7S2u\x99\xda\x04\xa4\r\xf1\xfa\b\xe1\u030e\f\x18y\n\x81`h\x9f\xa78\x8fJ\xf1|Ql\xdb\u00b6LT]\xa7\xeb\xe1c9\xe6\xd6\xff\xe6\xa4\xfb_\xe5\xc8I\x8f\xd2\xf6\xb0\xa3\x89K\x95XC\x84x4J\"\x9e aX$-\x97\xdep\x81\u0652\x8d\x11\xb7\xe5\tt\x9c\u032esZv\x9fc\x81K\xb9x\xd7@\ua512p\u007f^U\x04!\x9fJ\u062f\xa0\xb9t\x94\x94\"\x15\xad{\xf9\x84\x14\xf7\xeeF\xf86\x8d\a\x10\xd1d\xb3\xf3\"\u007f\xe7\xff\x92h}\x9c.\x87\x1c\u00a8\xf3~\x8bP5\xca\xcb\xcaPT\x95\xac\xac,TEK\x9dNjkk)))\xbdp\xf6\xec/\xdb\x01l\u06f6]\xb4x\x9a\xe5\x99g\x9f\xb3\x00\xca\xcav\xf6/\u06f5\xcba\u063c\x12iU\x03\x04\xd9y\xbd\xc9\xc9\xebC<R\x8fe\xd9\f\x19s1\xbd\x06\x1d\x8f\x10P\xbam9\x1f\xbd\xf9\x14s\x96l\xa7\xc1\x0e\xa1i\xe2_f\u03dd\xe6\x04&'\x9dt\n\xf7\xdcs\xf7\x8f\xf2\xc5?\x06\xe8?\xc5t\u9847\x1e\xe4\x92K.\xe1@v\xa4\xf9)@\xae\x00\xe5q\u061c\b\xf0\xf1\xbc\x15\xdc}\xef\xfd\xacX\xb1\x12P\x11\x8a\xea\x1e\xad\x1d\xe9\xd5\xffg\xef\xbc\xc3\xec\xba\xcas\xff[k\xed}\xca\xf4\xa6Q\x1b\x8dz\xef\xb2\xe4^\xe4&\x9a\xc16\x98bS\x13'\xb9$@n\x02\x81\x9bK\x80`r\x93Pb_\u01d8N\x8c1\xc1\xa6\xd8\xc6\xd8`\x1b\xdce\xc9Un\xea\xbd[3\x92\xa6\x97SvY\xeb\xfe\xb1\xf6\xdeg\x9f\x11\xe4I\x00\x97\x9bG\xdb\xcfQ\x9b\xf1\u0319s\xd6\xfe\u05b7\xde\xf7\xfd\xde7\x88\f\xfd\xd3\x0e\x82'\xaf\xd7\xcf%bpCZ=\xb6\xd1\xf1\xa3\x1a(\x93Q\xf51\xc8\x04vAT\nc\xf2o&B\xbeM\n_\x8f\xc6\xe5u<Q\x1a\x82\t5\x9e\xa7\x99<\xe74\xa6\xcd]I\x18\xd9b\xa4=k\x84\x89C\xb25~\xa9\x88\x92\x92I\xf3Wp\xdeG\xff\x915\u007f\xff]\x96]\xf51\x1a\xa6\u03a30:\xc2\xe8\xd0\x10^$\xdf\xd3)\xbc;\xfd3hS\xe9\xfc\xd3D\xa4\x88Z\xc2@\u06df'\x96 \xc6\xcf;L\x15\xda\x18v\xc9(IcVQ\xe7\xcadZ\xb6\xea\xebF_#\xd4\t\x1f\\\xb5\xa9\x98\xa4&\x98\n\xff`H^w\x81\x88F\xfca\u025a\xb73\xf7\xf4\xf3)\x15F9v\xf4\x18\xb5uu\xe4\xf2\x15\xaf\xa7R\xa9\u012e]\xbb\x1a\x8e\x1e=\xfa&\v\u00fe\u05fc\xae\x8b\xf9\x97\xaf\xbdV\x00\xa11&\u007f\xe4\u0211\xb3b\xb7\xc2\xf8RN\x06!\x04\xad\x9d\xf3i\x9c8\x13\xaf8\x8a_.R\xd78\x8e3\xde\xf0?h\xef\x98\aF\xb3\xe3\xf9\xfb\xb9\xf5\xa6\xaf\xf2\xd8\xe6>|\x91'\xe3\xfe\xfe\x05\xdd\x16\u0590\xab\xaf\xfec\xae\xb9\xe6s\xbf5\xc9=\u059d\xff&<=)\x96\xd1b\x1e[\xd8?\xf3\x99O\xf3w\u007f\xf7\xbf\xd1:\xa4T*\xbc&\xa3\xfcR\x80#\xa0\xab(xn\xc0\xe5\u0387\x9f\xe1k_\xfeg\xb6n\u0708\x9b\xab\x01\xa5\b4\x14\xfc\x900\xb4\xb8k\x18j\x82P\x13\x84!\xfad\x87\xfe\xbak\u03f56\b)m\x94\xdb\x18\x9e#5\xf4\x99\xae>VK.\x04\xa1\x11\x94C\x13\r\xc8\u0204(\x14X\\8^\xa2\xb1ml\x8c\x9b\xdb.^R\x1e-P\xd78\x89\x99\x8bW\x93\xc9\xe6\xd1:@D.\x82\"ZpZ\a\x94\x8b#\xd45\xb6p\xe6{?\xca\x1b?\xf3u\x16\xbd\xfd\xc34L\x9a\xce\xc8\xd0\x10##C\x16\u0089\xbeYhD\x02\x9d\xa4\xbb^\x13\xad\xdfT\x13\x9e\xa8s\u049d\xb1I?\xe2\r \xfa=\x88\xb2Ok3\x92\xa6\xbc$\xe7V\xfb\x99W\u03caT:}\x13\xd1\v\"\xe9\xf1I\xac\x12td\x8f\x9bl>1m\x10\xe1\u07e5B\x89\x96)\x93Xu\xe9{\xc9\xd55p\xac\xbb\x8b\xd1\u0451*\xe5[\x18\x06\x1c=z\x8cm\u06f6\xaf\x8c\xbf\xff\x81\x03{_\x9f\xc5\xdc\x18\u00e7>\xf1\t\x03\xf0w\x9f\xfe\u07e7\xbd|\xa4\xfb\xb2\x91\xe1\xca\b\xbd\x90\x12\xa4$W\xdf\xc8\u0139\u02e9ik'\b\x03\xb4\x0e\xf1J\xa3L\x99u*\xe7^\xf2\t\x9a\u06a6\x10\x06\x05\x9e\xb8\xef&\xbe\xf1\xb5\x1byd\xe7\b\xa3\xa1\x83#\x19\xb3\x8c\xff\xebW\xa9TB)\xc5\xff\xfc\x9f\x1f\xe7{\u07fb\x89\u014b\x17Uu\xe4\xe9\"\x1dc\x95\xe9\xa9Q\x1b\u079c!\x93\xc9\xe0\xbanR\xe0\xdb\xda\u06b8\xf1\u01af\xf2\x0f\xff\xf0\x8fH\u9f22.\x90\xff\x99b\xdeW\xd2<?\xe8p\xdfcO\xf0\xed\xcf\xfc%\xcf?x\x173O=\x8f\xa9KO\x87\xd0\xc7\vBzF<\u02a1N4\xf3\xf1\xe3dw\xfe:\xea\u0205\xb0\xce\u007fA\x80T\x0e\x8e\x9b\xb1\x1dfU'k\x12\xfd\xb8\x89\xd4(\xb1\xaf7\b\x0e\xd1)\xb8\x00\x00 \x00IDAT\xbc\x10\xbc0]\xa9l\x9foa\v\x91H\x1b\xd3V)\u0698\n!\x1a\x1a\xfc\x92\u03d4\u06671a\xdaB|\xaf\x94$\xf9H\xa5\xec`\x8d\xef3e\xder\xde\xfa\u026fp\xea\x87>E\xe3\xf4Ex\xe5\x12\xa3\x03}\xd6O\x1dY\xe9\x9c\xe3\xcd\xc4T\to*\x8a\x14D\x82g\xc7\x18\xb5I\xc9\x10\xc7v\xec&\xc5\x1b\x84\x91\xea\xa4!\xa7h\xcaI2R$|\x90H\x1a1\x128FF\x9b\x99\x92\x15,\x9eH\xd3c\"\xf9\xa7\x8eI\xe4D\xdeY\xed\x83\x1ek{\x02\x0f\x96\xbd\xf1\n\u67f3\x86r\xb1@oOo\xd5ty\x18\x86\x94J%\x8e\x1d;\xfe\x16cL-\xc0\u05293^\x9f\xc5<%\xe9k\xda\xf7r\u05e7\xb6\xed\u0611>'\"\xa4\x83\t\x02\x9a&Lg\xe2\xdceh\x13\xa0\x85u?\vB\x8fR\xa9\xc0\xac\xa5o\xe0\xd4\v\xff\x84\x9a\xbaf\xfc\xf2\x00\x8f\xde\xfe5n\xfa\xee-<q0d0*\xe8R\xfc\xee\x05]\b\x81um\f\xb9\xf2\u02ab\xb8\xed\xb6[\xf9\xc8G>\xc2\xf4\xe9\xd3S]9d\xb3Y\x9a\x9a\x1aijj\xa2\xbe\xbe.\xc9\xf6\xcc\xe5r\x94\xcbeJ\xa5\x12\x9e\u7854\xe2\roX\xc3]w\xdd\xc9G>\xf2\xd1h\xc3(\xbcf\x8e{BX\xc2wg1\xc3\xd3[\xf7q\xff\xb7\xbe\xcc\u02db\x9f\xa6\xa1m\"\x8b/y?\x8d\x1d3-\xfc\xe3\xfb\x1c\x1f\x1a\xa5\x14D\x04X\xea\x11\x17\xf5\x93\xd7\xeb\xe3\x9e\xf2<\x9fR\xa9\x84\xe3\xba(\u05ed(G\xd2 \x8c\xf9\xcdx\xbb\x01J\x91\x17K\xacp\x11\xd1\u007f:\xe9ze\xa5\x80\xa5\xfd\xcbM\xe4}n\x04\xe5B\x89\x9a\x86\x89L\x9fw\x16\xd9\\\xad\xf5K1\x10\xfa>Fk\xe6\x9d\xfd\x06\xde\xf6\xa9\u007fa\xd6y\x97\xe0\x87\x9a\xc2@\x0f~\xb9\x80W\xf6\b\xcbeL\u08c3\xc0\x8e\xe9\xfb\x1e\xc6\xf70\x81\x1f=\x02\xc2 \xb0|@h\x1f\xc6Da\x1b:\xd2\xc2\xc7\xda\xf0\xdf\xc2+\xc4\x1d\xb7#\x05\rYI]F\xe0\xa4D7\xf1C&\x9dw\xf5C2\xb6#\x8f\xcf?qa\xaf\x04ZXX&\x9e4\x15\xc9\xe6\xe9{\x1e5-\xb5,\xb9\xe82\xeaZ\xda\x18\x1a\x1c\xc0\xf7\x03\\\xd7M\x92\x95FGG9x\xf0\xe0\x94M\x9b^<#~\xfe\xaf;5\xcb\xc3[\xf6\x8b\v\x16N3\x00\xff\xfa\x9d\xef]\xbde\xcb\xd67\rG\x86N\x90J\x06\x91\x92q\xd3\x17\xd1\u06b9\x800(\x81cU\"\"\x14\xf8\xbe\x87\x11\x92\xc5g\xbd\x87\xc2H\x1f\xcf=z3\u0151\xe3\xfc\xea\xe6/\xe3\xe6\xf2\x94>x\x15+&\xb8\x8cw}\xa4\xb0\xbb\xf0\xefz\x83\x94\xcbe\\\xd7e\xe1\xc2\xc5\xdcp\xc3\xf5\xbc\xfd\xed\x97r\xc7\x1dw\xb2a\xc3s\xec\u07bd\x87\xa1\xa1AFF\xc2\u0102\xd3J\x11\xad\xe6<\xf6[\x99?\u007f\x1e\xef~\xf7\xbb\xb8\xf2\xca\xf7\x90\xc9\xe41&xM;r\x00\x13\x84\f\x84.On;\xc0/\xbfs\x1d\a\x9e}\x84\x9a\xa6q\x9c\xf1\xa1O\u04be\xf4lv<\xf5\bB(|\u07e7o`\x18?4d\x18sl\u0546\x93H\xcb\xeb\u5494JE\x86\x87\x87\xc9\xe4\xf2H\xc7M\x06\x85\xa2\x066\x1a\x0eJK\x11\xa3\xc6\u0280\xa7\r^\x84K\x88\x04\xaf0\x952&Rmm\xbcE\xa4\x8c\xa9t\xd4\xf6*\t\x81\x1f0g\xe9E\x1c\xd8\xf1\x14{\xb7=\x89T\x0e\x99\\\r\v/x\x1bg\xbc\xe7\xcfh\x1c\xdf\xc1\xc0\xf0\b\x81\xd6\bc\xbd^\xb41\xd6* \xf4#\xff\x15\"\xf5L\xb4\xa5\b\xfbg\x1d\x85B\x04\xa4\xa2\xdd\xe2\u009a\xb4\u0572B\x06G?_<\xd9i\x8c\xc1U\x82\u01ac\xa4&##\xe3\xac4\x142\x16\x86\x8a\xbfG\xdco\x8b\xb1\x1fN\xe1\xf9\x95\xff9\xdeFu\xca\xda#\xde\n\x851\xf8e\x98s\xe6E\xb4\u03d8\u01de\r\xebl\u4723\x92\xc2S\xf6\xcatww\xf3\xd4SO/\x06\x1e|\xdd\x15\xf3\rG\x06Y9\xa9\xd1\x00\xf4\x1as\xee\a\xaf\xfc\xe0\xff\xdal\x13\xae\x13xEHE\xe8{\u050d\x1f\u03d4%\xe7\xe1d\xea\t\x87\aQ\nK\xb4hkG\xe9{%2\xd9ZN\xb9\xe0j\u02a5a6=\xf9\x13\x06{\x0e\xf2\x8bo}\x01\xad4\x85\xf7\xbf\x8f\xa5\xad\x19:\xb3\x1eY\x19a]\xbf\xe3\xf3\xb6\x1e\xe7v\xe7\xbc\xe0\x82\x8bY\xbdz5\x9b6mb\xe3\xc6M\xec\u0673\x87#G\xba\x18\x1c\x1c\x8cB\x9a\xb3\xb4\xb5\xb5\xd1\xd99\x85\x8e\x8e\x0ef\u0318\u03aaU\xab\x90\xd2\xc5\xfa`\x17-\xae\xf9\x1a\x16\xf20\b\xd02\xc3\u04fb^\xe6'_\xfb\x17\xb6\xfc\xf2\x87\u0535\x8c\xe3\xd4\x0f|\x92Yk\xdeE\xc1\vP5\r\xb8\xf9:\xfcr\x99\x91\xc1~\xeb\xe1Aug\x1e\xc7n\x9d\xbc^\x1f\xd4\xe7\xf0\xf0\b=\xbd\xbd\xe4\xeb\x9a\xc8ds\xb6\u0324\x8a\x95I\xad9\x19\xfd]G\x05\xa7\x14X\u065d\x13O2#\x11\xd1{l\x03'D<\u0493*\x9e&E\xbbF\xe3\xf0\xc6\xe0\x97\xcb\xd45Mf\xde)o\xa1\xeb\xe0V\x86\xfb\xba\x19?u>K\xdft%\xf9\xb6\xc9\f\x0f\x0e\xa0\x84 @F\x10\x8d\xd5\xc0\xa3\xed0\x91\x91\xca\x16\xed\u0529!\xf6d'5\xa1j\xf9\u0644\xf2M\xac\tb\x1e\xc0\xaau\xe2\xc2n\xfb\ua332\x1dy\x8d##7\x9aJ\x017T|\x1f\x05\x95\x13J\xfa\xa3\x95Bn\xaa\x9eK\xe5\xb3L\x15$\x14\xff9q\x1e\x8e\x88P\x11h\x9a&\x8cg\xea\x92S\xd9\xf7\xfc\x13\x14K%\xb2\xb9\xbc\xb5\xda\xd5Vd\x10\x84!\u01cf\x1f?\xfdu\u05d9\xef\xe9\x1fefsm\f\xaf\x9c\xfb\u037b\x1e\xfc\xf1\x86'\u058d\u04e1\x9f\x9cq\x84T\xc9\xca\x1b?\xfb\x14\xa6,:\x87\xb0XB\x87\x1aGY\xb3\x04-L\xf2\x92\x95K\xa3dk\x9a8m\xcdG\b\xfc2\x9b\x9f\xbe\x83\x81c\xfb\xf9\xc5\u05ef\xc1\u01e7p\xe5\x87\xe8k\xc91'W\xa45+\x12,\xeew\xc5\xf9-1j!\x93\xa5KW\xb0t\xe9\n\x00FF\x06\x19\x19\x19\x89\xc6r]\x1a\x1b\x1b\xc8\xe5*CGa\u8f6a\u0650\xff\xd1\x15\x04!\x197\u02c6]\x87\xf8\xfa\xf5\xd7\xf3\xdc=\xb7\x92\xcd\u05f0\xec=\x1fc\xe6EW\xe0\xf9!\u0294\xc856\xe3\xe6k)\x0e\xf7S\xec?NV\x1a\x1b\xfe\xabu\x82\xb1J%\xff\xdbD\xc8\xfd\xff]\xc6\xed\xd5;8\xcc\xf1\x9e~\x1a\xa7\xce%\x93\xaf\x89\x94 i( E\xeaI\u06f9\n$A\b~d0\"\x84\xed\x85e\xba+\xaf\xa0\xc8X%u\x1c\x9cV\t\x84H \x17\xec\x1a)\x97Kt\xce=\x8b\xa9s\u05f3\xfd\xf9\xfb\x18:~\x84\x83[^$\xdf1\x93\x92\xafq\xa4\xed\xae\x8d\x01\x95\x10\x93\xd2B$&LHY\x11\xdd{!V\xab\x9eH$\x11\xa9gd\x12\xfc<)\xbcFG\xa7\tk\u022e5()\u027b.9iU'\bQ\u054d'\xaf\x8d\x11U\xc4\xe5\xd8\x12\x9d*\xf7c\u0781\n\x8e.\x10U\x9b\x83\x89\xbe\xae\x10V\x11#\x02\x8d\xceH\xa6.YE\xbe\xbe\x89rq\x04'\x93\x8dN%!Zkc\xb4\x16CCC\xf3_W\xc5\u0718\x12k\x9e\xc9\xc6Eq\xfc\xbac\xfe\r\xb7\xdf\xf6\x83\t\xdd\a\xf7V\x93\x9e\x80\x0e\x03jZ\xc73\xef\r\xef%\xdf\u070e\xd7\xdfoM~\"\x12\"\x99B\x8e\xce\xfa\xa5\xe20\xf9\xfaq\x9c\xf1\xe6\xbf\"\fC\xb6m\xf89C\xc7\x0eq\xff\x8d\xff@qh\x90\v\xde\xf7aF&7\xb2\u0414\x98\x90\xd5\xd6\xdfY\xff~\xe4\xa8\x1d\xd7\x1fM2@\xf3\xf9<\xb5\xb5\xb5\xc9\xc0\x90%0*!\x15\xaf\x97\x82\xa7\xb5!\x93\u0272i\xdb.\xbe\xf4\xa5\xebx\xf4\xee;\xa9o\x19\xc7\xe2w\xfe\x053/~\x0f\x81\xd6\x04\xbe\x87P\x92l}+\x99\xda:F{\xbb\x18\xe9=\x8aB'\xa4T\x95N\xf9\xb7$'\x9d\xbc^E\x80%:\x1d\x1d:\xd6\xcf\xf0\xc80\x1d\xcd-dr\xf9\xc8\xe7;\x9ar\x8c\x8b\xad\x88\x8ap\u0535J\xa1\bBA \x05J\tT Q\x0e`4\"\xf0\xa3\xe2])H\xf1z\xb6\xfauRU\xb0\xe2\x96\x1e\x1a\xf0}\x8fL\xae\x81\x05\xa7_\u0391\x83\x1b\x19\xe8\xda\u03ce\xc7\xef\xa5}\xd1*r\xed\x93\x18-\x160\xd2>w\x19\r\xed\x84F\x83\x88\x02'\xa4B\xb9\x0eJ\xaa\b*1Ud\xa7-\u4552jL\x94\xc7\x19oB\xe9IS\xc0\bI\xc6U\xe4\\\x85\x92\xd2nV\xc6$Dp\f\xef\xc2\u0621\xb8\x13;\xed\xb1\x9dz\xa5\xc0\x9b\x04\xd3\x1a\x13\xa7Q\xe9\xe7\xa3\xc1+\xad\xed\xfd4n\xfa<\xf2\xf5\x8d\x14\x87\xfa\xad\x8b\xa2\x90\x84\b\x84T\xa2X*q\xe0\xc0\xc1\xe2\ub998\x1f\x19\x1a\u5fae\x8c|\xe04\xa1\x8d1\xe2%\xc3\xf5?\xf9\xe1\x0f\x97>t\xfb\x0f\x13\xcf{!\x95\xf5i\xd0\xf6X8\xed\u07372\xe1\xac5\x94J\xc2\xfe`\bth\xb1<\x19\xb9\x02\x89T\x01)\x95\x86\xc97\x8c\xe7\xccK\xfe\x1a'\x93e\xcbS\xb73\xdcs\x84\a\xbf\xf3E\x06{\xba\x19\xfd\xd3O\xe0\xcd\xef`\x8e\xf6\x99\x9a\xf3\xad\xe5\xe5\xefY\xd0\xd3E\xda\xf7\xfd\x13,q_OE\xdcz/;\xf8\x81\u6a67\x9f\u57fer\x1d\x0f?\xf0 \xcd\x13\xa7\xb0\xfc\u028f\xd1q\xe6\x9b\xd1BP.\x95PB`\xb4&\xd7\xd4F\xa6\xa6\x1e0\f\xf6\xf5R(\x14\xc8fs\x95\xee$\xf2\xa989\xe2\xff\xdaw\xe5J)\x86\xbc\x80\x03]\xc7\b|\x9f\xda\xc6\x16T&G\x10\x84)\x89\\\x05K0\xc9H\xbfD\v\x89'\x04\xc2u\x11J\xa2\x82\x10\x95q\x80\x00\x11D\xbai\x01&\x12L\xeb\x04\xfb\x15\t\rZY\xe7\x95\x13\x80\x1fj\xf0<\xc6MY\xc8\xf4E\u7c79\xff(G\xb7?\u03c1\rk\x99\u007f\xc9{-\x84a\f\xa1\x16\x84R \x94\x83\xe3f\x11\xcaE\x1bM\xe8{\x04\xa5\x12^\xe0\x11ze\x82\xe2\bAq\xd4\x12\xa0~\x19\xed\x951:@\x87\x9a0\b\x18\x1b\x9cb\xa2\rF)\a\x95\xc9\xe2\xd4\xd6S\xdf\u0502jmA\xd7\u0510\xc9\xe5psy\x9cl\xce\x16\xd70\xc0\u8832Y\xa5\x02\x9f\xd3K\xbc\xba\xdb6\x15\xc8)!\x98+\xffF\x15\x1cS\xbd!\xc6\xde0u\ud4e8il\xa6\xf7\xf0>\xb4\xd6(\xa9\x92S\x8e\xe7\a\f\f\x0e\x8a\xd7M1\u007f\xf28\xe2\x1d3\xa56\xc1\u05dc\x8d%n\xb9\xef\xd7O\xbe\xe7\u07ef\xfb\x02\xc4QSR&]\xb91\x9a\u018eY\xcc~\xcb\x1f!\x9b\x1b(\xf4\x0f\xa2\xea2\xa8Q{,\x11\x84 t\x12\x1b\x15\xbfa\x00\xe5\xf2(\xb5M\x139\xe3\xd2O\x92ije\xe3#\xb7P\x18\xe8a\xfd\x0f\xbf\u03b1\xfd{\xe8\xfb\xcb\xcf1\xb0r%\xbdy\x98\x93/\u04d4\x93\x04z\xec\xfe\xfa\xfb\x17\xf6\xd7\xd5\x15u\x01n&\xcb\xe8h\x81\xbb\xef\xfe%\xff\xf7\x86\x1by\xe1\u0157\u8637\x843\xde\xffW\xb4/_\xcdh\xa9L\xe0\x95\x93\x13\x8f\u059a\x9a\xd6vj\x1a\x9a\x018\xde\xd3C\u05d1nf\u035a\x811\x12\xc7Qd\\'\xf2\x948YP_\xebj.\xa4\xa2o`\x80\x03{\xf7`\xb4\xa6\xb1}\x12nM=\x85b1\x9a\xb4\xa4\xe2\xfd\x1f\xfb\xa4 \"\xfbWM9\x10\x11\x84\x11!\xe2R!\x1d\x89p\x01\xcfD\u49b4\xddx\nX\xf9M+>\u0351\x06\x81G\xb6\xae\x819+\xde\xcc\xcb;\x9f\xe5\xf8\xa1m\xec]w/\x13\x16\x9dJ\xe3\x94\xe9\x84a\x80v\xf3\xb6\x93/\x15\xf0\a\xfb(\r\xf53r\xfc0\x83\x87\xf70\xdau\x90B\xcf\x11\xcaC\xfd\x04^\x89\xd0\xf7\xac\xef\x8b\x0e\xaa\xa6[#l&u?Vj\x83\x88\x88P\xe1\xb88\xd9<\xf9\xfaF\x1a\xc6M\xa0\xa5c:\xadSg3n\xfa|\xea\xdb'\x90ol!_\u05c0\t\x03|\xaf\fJ\"\x1d\x17\x19\x9f\f\xc6\xf4\xdfT\x9dE*8\x8d\x10\xa2\xaa\xa6$\xf8z\xa2\x8d\xafl\x05Z\x83\x93\xaf\xa1\xa6\xb95B%Bdd\xa7!\x84\xf5\x8b\x17B4\x19c:\x84\x10\x87_\xd3b~\u02ce\x11\U0004e675\x06`\xb3\xf7\x17\xff\xf8\u0736\xc3W\xde\xfc\x95\xcf\xd3\u007fx\x9fA\b)b\x9c<\xfa\x81\xa5\x94L;\xf72Zf/\xc5/\x87h)\xd0\rY\x8c\x12\xa8\x11\x1f\xe1\x03\xa1\xed\b\x8d\xd4\x15\x1aY\xc4\u064d\x05\u071a&V\xbc\xf5\xa3\xe4Z\xdby\xe9\x17\xdfb\xa0{?;\xd6\xfe\x82\xa1\xe3/s\xf4C\u007f\xcd\xe9o\xbe\x9c\xd1\xf6Z\xe6\x86\x05&\xe4\xad\xd7q\xf0\xdfP]g\x8cA)\x85\xeb\xe68t\xf8\x10\xb7\xfc\xe0\x87|\xf3\xdb\xff\u0191\xaen\xe6\x9c~>g\xbc\xf7c\xb4\xcf[\xc1\xf0h\x01\x1d\xf8\xa9\xde\xc1\xfe\x9aoh\xa2\xb6\xb5\xddb\xb1\xbd\xbd\x1c>|\x98\x85\v\u7864$\x93\xb1\x99\x86\xe2d_\xfez\xe9\xcd\x19\xea\xef\xe3\xe0\xee]\xb8n\x96\xfa\xb6v\xa4\x9b%\x18\x19E\vI\xd97\x14\xfd\x10)\x05\x19G\xa2\x94\x81\xc0\xe0\xa1)\x04\x10b\x13\xe5\x851\xc8x\xbc\x11cG\xcc\x1d\xd7f|\x1a\x1dy\xa3\xc70\x86I\x8a\u007f|*\x8d\x15(\xb1\x02\xc4\x18\b\xca%\x9a\xdbg2k\xe9\xc5\f\x1c;\xc0\xb1]\x1b9\xf2\xecZ:f\xcdg\xa80L\xcf\u07ad\f\x1c9\xc0\xe0\xa1]\xf4\xef\xdf\xce\xd0\xe1=\x94\x86\xfa\b\xcb\x05\x82r\t\xed\x97O \x19S~\x1fv[\x12\x95M\xaa\xfa\x95\x89\xe0 \x1dF\xb8\xba\xfd\x8c.\xa1P\x99\x1cN&\x1b\u017e\xcdb\xf2\xc2S\x98\xbc\xf0\x14\xc6\xcfZ@\xe3\xf8\xc9\xd6\u07e6P\xb0A\u03ae\x83P\u059a7\u0749\x8b\xb1P\xcc\tR\xd0\x18\u007f7i\xda8\xf9\fk\xaf\xebP\xd3\xd8\x12y\xcfW\xc3\x05e\xcf#\x9f\u02f5\x06~i\t\xf0\xda\x14\U000d738d0\xec#\u039e\\g\x00^\x1a6\u007f\xbd\xad\xab\xf8\xa9[\xbez\x1d;\xd6\xfd\xda\xc4o\x03\x91#\x98\x10\x02\x1d\xf84\xcfX\u0234\x8b\xae@\xe5k\xf1\v\xc3\b#0J\xe1\xd7f\xd0\x02\x9c\x11\x10:\xb4\x93V\x91-g\xa5\x15\x10\x18!(\xfb\x05d6\u01e2\v?Dm\xcb$\x9e\xff\xf9W9\xb6\xfbE\xba\xb6=\xcf\u03ff\xf4I\x8el{\x91s\xdes5\xbd\v\xe617\xf4\x99]\x13P\xebZ\xd8E\xff7\xaaL\xb9\\\r\xc6h\x1eyl-\xdf\xfc\u6df9\xff\xbe\xfb\xf0\x85\u00eaK\xdf\xcf\xf2\xb7\xff\x11\u0353gP*\x8cb\xc2\xd0\x12P\u046b\xa8\x845br\x1c\x87\x96\xc9S\xc9\xd5\xd4s\xb4\xbb\x9b\xbd\xfb\xf6R_WK\xb1XD)\x99`\u007f'\xaf\xd7\xfe\xf4\x050\xd8\xd7\xc3\xc1\xdd;i\x1e\xd7NC\xebx\xcaAH)\u0414BC9\x88\x92\x854\x94\xb5\u5374\f\xf0\x8dU\xb0\b'\x9a\x88\t+\x86[\xf1\x9b+\xa5\x8bQ!\xda\xe8$9\xc8\xf2U\"\"\x11+\u05b8q!O\xab@\x02\xcf#WS\xcb\xf4\x85\xab9\xb0u\x1d]\xfb^b\xff\xd3\x0fP\xd7\xd0H\xcf\xf1\xc3\xec\xdb\xf0(#\xdd\a\t\xfd2&\f\xac\x1f\xba68\xb9\x1c5m\x13\xc97\xb7\x93kh\u00adm\xc0\xc9\xd5\"39\xa4\x93A(\x85r3\xb6\x19\x14$]x\x02\x99':p\x8d\t<\xfc\xd1aJC\xfdx\xc3}x\x83\xbd\f\x1f{\x99\xe2`?\xa5\xe1~\x06\x8f\x1cd\xff\xb3k\xc954\u0471x\x15\xb3\xcfZ\xc3\xf4U\xe7\xd2\xda9\x9b\xc0+\x12\x94KH\xa9\x10\x8e\x83t\x1c\xdb\u96f8\xf5\x11\x95\x01\xac1\xc7\xfcx\x8f\xd1\x11dc\f)\x8d\x8e\xfdM*E\xae\xa1\xd9\xdaf\xe8\x94\a<\x10\x86\x1a)e\xaeX*\xb6\xbc&0\u02c6\xae\x11\x06=\u0139\x1d\xb6\x90\xef\xf3\u037b6\xf5\xf0\xa5{~\xfa#\x1e\xfd\xc1\x8dQ\xed\xb6lf\x85L1H7\u00cc\x8b\xdfc\xbb\xf2\xe2h\nB\xb1\xdd{X\x13\rA\b\x0f\x8cF\x9a\xc8\xf6&u|\xb4\x83jV\x1a%1\xccX\xf5f\x1a'Na\xe3\xbd\xdfb\xe7\u06bb\x19\xed\xeb\xe6\xf1\x1f\xdc\xc0\xc1-\x1b8\xe3]\u007fF\xd7Eof\xb8\xb3\x89Y\u01a7\xcd\r\xc8\bC`\u018c=\xff\u007ft))q\xdc\f 9t\xf8\x10\xff\xfe\xc3\x1fs\xcb\x0f~\u020e\x1d\xdb\x197y:g\xbf\xe9C\u033b\xe0r\u0716F\xfc\xe20FWf\xd6\u048bPk\x8d\xc20\xaes\x165u\r\xf4\x1d{\x99\x9d\xbbw\x01\xe0\xba\xcek\x1e\xa0q\xf2\xaa\xc0{\x18\xf0\x03\x8fC\a\x0e\xd0\xdf{\x8c\x19\x8bW\xd0\xd0:\x8e\xc1\xd12\u00de\xcd\xca4Q\xa1\xd3X\x13-\xdf\x18L\x18\u0372K\xa7\"\xdf3\x06\x19\x8e\t\x95\x90\x02\xa5\\\v\x8b\x1aM\xa8u\x05h0Q\x11OKB\xc4\x18Y\x9e\x81Rq\x94\xe6q\u04d8\xb5\xf4\"z\xbbw\xd3wd7\xeb~p-~\xb9\x801\x1a\xe1\xb8dj\x1b\u0237\xb4S7\xbe\x93\xda\tS\xa8\x9f4\x9d|\xebD\xf2\xcd\xe3\xc8\xd67\xe2\xe6\xebPY\xab\x9f\x17\xcaI\b\\De\x9c^P\x19\x15M\x80\x11a\xe1\xa1\xd0+\u23ce\xe0\x8f\xf4#\v}\x04}\xdd\xf4\x1f9H\xcf\xdem\x1c\u0779\x91\xc1\xae\x83\x14\xfa\x8e\xb3\xfd\x91{\xd8\xfb\u0323t,Y\u0172\xb7\\\xc5\xfc\xf3\u0782[S\x87_*$Cs\xcaq#hX\x9c@pV\xce\x04\xd5\x1fM \xb1\xb1p\xba\x14\xb8\xb9\x9a\x14\xa9\\)\xf4\xae\xe3\xe0y\xde`}}\u04de\u05e4\x987d`e\xab-\xe4]\xa5\xf0\x8am\xa3|\xef\xf1\xc7\u05b9w\xff\xeb\xe7\xed@\x80\xdd\xee+\x18\xb3\x90h\xbfD\xc7i\x173\xe3\xe2w[\x02\xa4\\Np\xf4\xcaiE\x12\xe4\x15h\a\xe9Ysb\x13\x92\x1c\xe9*>\x956\xa7P\a\x1e\xda\xd3LY\xb8\x8c\xf1\xb3\xff\x0f\xe3f\xce\xe6\u017b\xbe\xcf\xe0\xd1\xc3\xec}\xf61\x8e\xef\xdd\xc1\x8e\xf5\xbf\xe6\u0eeef\xe5\x19g2\xbb)\u03f4|H\xb3\xf4QBW\x19\xe9\xbc\xde\x0f\u064e\xa3\x90\u02aa\x85F\xcbe\xee\xbc\xeb\x1en\xb9\xf9\x16\x1e_\xfb\x18A\xa0\x99\xbb\xec|\x96_\xf8>\xa6.9\x9b\xc0\u04d4\xba\a\x10\xf5\n2\xc2\x06\x16D\xb7g\xec\xe6\xe1\n;X\xd1:u6nM\x1d\x00\xbbv\xed\xe6\xe0\xe1\x97\xe9\xec\x98L\x10\x04'%\x89\xaf\x13(MH\xc5\xf0\xf0\b\x9b6m\xa1T,2\xb5\xb3\x93\xd6q\xed\xec\x18\xf1\b\u3390\x8a_\xb9L\x95\x1d\x13%V\xc4\xf7\x9a\x88\x9d\x12\x13a]T\x18\xa5\x83t2h\x1d\"D\\pL\xc5L\xcaD\xb2A\xf1\x1f\xc5,\n\xa6\xcd=\x8b\xfd[\x1f\xe7\xf0\x9egq\x9c,H\x89R.n\xae\x96|\xdb\x04&\xad\xbc\x80i\u7f55|\xdbdT&\x17\xf17!h\x8d1\xf6w\x1d\x04\x10\xf8Ie\xb4\xcf\xddDj\x96\x8az%\r\xbb\xe80@\x87>N6\x87\x93\xef\xa0&;\x9d\xa6\xba\x1c\"\xf0\x18\xed=\xc6p\xf7A\x8e\xed\xde\u010e\xb5\xf7\xf3\xf2\x96\r\xf8\xc5\x02\xbb\x9f|\x90\xae-/px\u04f3\x9c\xfd\xa1\xbf\xa6q\xfcd\xca\xc5QL`'beR\xd0S$\xa8H\x17\xf6\xdf\"e4&\r\xb1c\x8c@erV>\x99\xf8\x1d\xd9G.\x9bedd\xa4K\b\xf9\xe4kR\xcc7\xf6\xc7<\x9an}|X\\\xf3\xe2\xf6\xae\x9a\x9f|\xe5\xd3f\xb0\xeb\xa0H\b\xcf\x18^\x91\n\ud5e9i\x9b\xc8\xdc\xcb\xfe\x94\x86)\xb3\xf1F\x86*\x8b\v\xa2\u022b\x10\x11\x86\xa8\xb2\xc6)\x9b(%E\x82\xd4cH\x97hzMG\xde\xceyA\xc00\xb5-M\x9c\xf1\u078f2i\xdeR6\xfc\xf4\xbb\x1cxa=\u00fd\xddl\xb8\xeb\xfb\x1c\xde\xfc\x1c/\x9e\xffVN\u007f\u06fbY\xbah\x01\xb3\x9asL\xad14\xcb2\xae\xd1\x15\u00de\xd7\u064d,\x85\u0779\x91\x19\x00\xfa\x8ae\x1eY\xf7\x14?\xbe\xf5V\x1e\xb8\xf7\x97\f\x1c;\u02b8\x8eY,>\xedm\xcc]y\t\xf5\xad\x1d\x94\a\x8b\x04\x04\xe0(\x8co\x90\xf5\n\x99\x17d\x1cA\x10V\x16\\F\x1a\x1c)h\x9c4\x95\xfa\xf1\x939\xba\u007f\a[\xb7\xed\xe0\x85M[\xe8\xec\x98\xf2\x9fr\x84<y\xbd\xf2]y\x18\x868\xd2ahh\x98\xe7\x9f\u007f\x01\xc7q\x98\xdc9\v\xc7m\xc0/\xf6#\x1c\x89N\x87\x11\xa7<E\"\xe64\x82)\xa4=\xf2\a \xab\xc8\xc4Ja\x92\xcaA*\x17\x13\x86\x15\x1dK2:o\x92oR=dc\x1b\x04!\x04\x81W\xa0e\xdc4f.<\x8f\xa3\x87\xb6\xa0\xc30\"g\xb5\xed\xd0{\x8f\xf2\xf2\xb3\x0f\xe1\xd6\xd41\xf3\xa2w\xa1\x85\xc0/\x17\xa3|\u0354\xd4PJT&\x8f\x8c|\xdb1a\x02\xf3\x98\xc8F1\fC\xeb\x03\xe3\x95(\r\xf6\xa12Yr\x8d-\x84h\x8c\xe71\\.bJ\xc36|\xa2\xa1\x89\xc6\xf1\x13\xe9X\xbc\x8a\x99g\xaea\xdb\xc3?\xe7\xb9;nb\xf8x\x17\xe5\xe2\bO\xfd\xf8\x1b\x18\xad\xb9\xe0#\x9f\xc5\xcd[{\x02B\u06d0\u02884\xae\xe80NT\xf5\xc4V\xf0\x90ruL\xe9\xe1\xa5\x148\x99\x8c}-\xc2\xd0Z\x13D\x03\x01\x8e\xa3\xc8d\xdc$\x14\xf8U-\xe6\xbbz\x87\xd5\xec\u05ba\u0418\xc1\x86u=\xe2\xd6\xed\xc7\v\v~t\xdd\xe7\u0341\xe7\x1eO\xde\b1&\xdeSe\xf3\xccz\xf3\xfb\xe9<\xe7R\xc2XQ\x01\x88\xc8s\x810\xc4)\x858\xc5\x00\xe5iD\x18\xb1\xeeJV\x8c\xf6\xab\xec\u0362\x1dR\x82\xca\b\x94+\tJE\x9cL\x8e\xd9g\xbf\x91\ts\x96\xb0\xf5\xc1\x9f\xb1\xe5\u05f7st\xcf6\xbawo\xa6\xe7\xd0^\xb6<\xf2K\u67bd\x86S\xdfx)\v\xe6\xcfc\xfe\xc4f\xe64\t\xeaM\t\x11\x1d\x0142uC\xbc\x8a\x1dX\xb4\x8d\x9b\b\xb7\xccf2\b'GIk\xf6\x1f9\xcecOm\xe0\xe7?\xbd\x9dg\xd6=F\xef\xe1\x83\xe4\x1b[Yv\xee;Y|\xe6\x15L\x98\xb6\f!\x1c\u02a5\x82\xbd\x81d4\xb5\xe0\t\x8co\xa0V\x92\xa9U\xe0(4\x90\x95$\xc9*\xd9\xc6\x16&/=\x9d\xbd\x1b\x1e\xa3\xa7\xeb0\x8f?\xb5\x81K\xdf\xf4F\x94\x14\x91v\xf9\xe4\xf5Zv\xe5ah0\x04\xec\u067b\x97\xed\u06f7\xd3:~25\xeds\xe8\xed\xf5Q\xc3!8\xc6\xdab:\x02\xa3\x04:\x8a\x8d\x8b\x83\"\xac\t\x96J\xf0\x10\x11\x18\xc4\x18\xab\u0724\xaf\x14\x12\xa9\\\xb4\f\x10Z\xdbD\xa2\x14\x962\x16m1\xa9\x1b\xc5\xda\xce\xda.{\xee\xa2\xd5\x1c\xdc\xf1\x04\xfbw=\x83\x92.\x06C\xe0{\xf8\xa5\x02#G\x0f2xp'RHf\xbe\xe9}dj\x1a\xac\xea#\f\u0441oC,\xfc\x80\xd2\xc0!\xca\xc3\x03\x8c\x1e=H\xb1\xb7\x1b\xa1\xa2\xf0\x94r\x11od\x88\xd2\xc0q\xbc\xa1~\xca\xc3\xfd\x04\x85Q\xda\x17\x9d\u0182+>L\xae\xa1\x95\x902\xa1\x86\xb2\x16d5\xc8\xc0\xa7<j=\xd5[\xa7\xcc\xe4\xec\x0f}\x9c\xb6\xa9\xb3y\xf0\xc6\xcf\xd3\u007fx\x1fR(^\xb8\xfb\at,=\x95eo}\x1f\xa5\xa1~k/\x1c\x1f\xdd\xe3\x13\u0258\xae;60K\xac\x86\x8dA\xc62\x03Qq\xbe!\xb1)\u0589Z(\xc6\xfa3\xd9,S\xa6t:\xafz1?6<\"\xda\xeb\xebB\x80g{\xf2\u007f~\xb4\u031a\a~\xfa\x03\x9e\xb9\xe3\xa6\xe8\x04oc\xa2\xd2\x15\u0284\x01\u35de\u02fcK\xff\x14\x95\xc9\xe0\x8f\x0eG\xedx\x88\tB\xa4\x17\xa0\x8a\x01\xaa\xac\x91\x9e\xae`aBF\x0f\x93`eF\b\x9bo(\rZ\x82\b\x81\x82F\xba\x122\x82\xc0/\xa3\u00c0\xba\xd6\xf1\x9c\xf6\x9e\x0f3\xeb\xb4\xd5l\xbc\xef'l{\xf4\x17\x1c\u07ff\x8bc\xbb7\xd2{p\x17\x9b\x1e\xfc\x193W\xadf\xd9\xf9o\xe2\xd4U+X2\xb3\x839-5\xd4\xe3\xe3\x86\x1e\xa1\x8e\xdd\xd1xE\xbbS\x13\x1f\xa3\x8dA\b\x83#\x1dT\xc6N\x95\xf6\x97<6m\xde\xcaC\xeb\x9f\xe6\xbe{\xeef\u02f3OS\xe8\xebFek\x99\xb9\xe4\x1c\x16\x9f\xf5N\xa6/X\x8d\x9b\xab\xc3\xf7\xca\x04\xdeH\xb5\x9d\\L^\x155\xc6\xd3\b\xdf \x1a!\x93\x93d\x95\xb4Y\x8aR\xa0j\ua63a\xfcl\x9e\xad\xfb&\x85\xc1>\x9ez\xf6yv\xbc|\x94\xb9\x93[)\x16K'@a'\xafW\xef\xd2\xda\xde\xf8\xe5\xb2\u01c6\r\xcf\xd1\xd7\xdb\u00fc\xe5\xe7\x90k\x9b\xc9\xc0P\x11\xe9\x1b\x84\xaf\xed@\x91\x12h\u01e0\x95\xc48\x12\xed`\x8b\xbcR\x18e\x87td\xa8\x11\xa1\xa9\xaaGBT\xf5\x93H\xa5\x90\xcaA\x87~\x95\x83.\txc\xaa\xb0|\x91\x82b\x84\x10\x94\xca%\x9a[;\x98\xbf\xe4|\xba\x0fo\xa30:\x80\xebf\xad\u076eT\x88l\x1eod\x90\xcdw|\x03\xaf8BC\xe7lr\r-d\x1b[\xc85\xb7\x93o\x19\xcf\xc0\xfem\xbcp\xf3?s|\xeb3\x04\xc5\x02\xa1WD(\a\xe5f-1*\x15\xd2u\"\vm\x89p\x9ch\x94\xbf\U00084170A\u03a5\x00\x1c\x19\xf9\x97\x87\x01\xc5\xe1\x01\xb25u,Z\xf3\x0e\x8a\x83\xfd\xdc\xfb\u5ff1N\xad\xe5\x02\xc7\xf6lG\x87aEzhl\xf1\x8d\xcd\xc8\xcc\x18\xe2uL4M\x85R\x88a\xa9\xb8\xe9\u0506\xd0+\xd9F+Eh\x1b\x03\xb9l\x96\xce\xce)\xaf\xfe\xd0\xd0\u06a3H \xdc\xdd3t\xf1^\xdc\xcf>\xb9\xfe\t~\xfd\xad/j\xc2 \x19\f\xaa\"\xd9B\x9f\xda\xf6\xc9\u033f\xfc\u007f\xd04m\x16\xe5\u0442\r\x8a\rBD9@\x15m7.\xfd\x10\xa1+\x13\x9fF\b\x84\x0e\xab(\x86\xc4`G\xa4\xce5\x1a(\x1b\x18\t!\u0087\xb5\x0e(\x17\x86Q\x8eK\xdb\xf4y\\\xf0\xe1\xcf0\xf3\xb4\xd5l}\xf8n\xf6nXG\xcf\xfe\x9d\f\x1c\xde\xc3\xf3G\x0e\xb2\xf9\xc1\xbbX\xbf\xec4\x16\x9cq>KV\xac`\xd5\xc2Y,\x99>\x89\xf1\xb5\x0e9\f\x84\x1e^`1\xae?dQ\x8fG\f\x1c)q\\\x17p\xf1\x81\x97{\xca\xec=\xb0\x8b\xed{w\xf3\u0533O\xf3\xe8\xaf\xee\xe5\xc0\xf6\xcd\xe0\x15q\xb2uL[t6\xb3\x96^\xc4\xcc%khh\x99B\xe0y\x94FG\"WCSE\n\x99h\xc4Y`\x03{\u0168&+C\x8c\x92hW\xe2FR,#a\u072c\xf9\xb4u\xce\xe4\xe0\xa6>^\xda\xf04O<\xbf\x89\xb9\x93/\xaa\x1a\xda:y\xbd\xfaW\x10h\x10\x92\xe1\xe1\x11\x9e|\xf2I\xbc \xa4u\xda|L];\xe5\xbe\x11{\u007f\xe8JR\x90\f\r\x9a\x10\u0436\xa0g#_\x11\t\xc65Hm!\x96\x98\xb4\x94P\x05\x1f\xc4\xe8\xafT\n\xa1\x1c\x8452O\xec]\u01de\x8eI\xe3\xc7\xd1}\xa9\x81\x92\xe73w\xf1jvo~\x94-\x1b\x1fAJ\xc7\u00ae\x91L\xd9\xc9\xd7Q\xe8\xed\xe6\xa5\u007f\xff\x17T&\x87[[O\u0744\xa9\xb4\xccZ\u0338\x05\xabpj\xeai\x984\xcdz\xc0\x14G\b=\x8f\\S+\xf9\x96v2\xf5\u0378\xf9:\xb2\xf5\x8dd\xeb\x9bps\xb5H\xd7%\xd74\x0e\xb7\xa6\x81 \xf0*\u0773\xb6\xb9\xa09mpb\xbc\x1d\xf0\n#H\xc7e\xd1\x1b\xae`\xcf\xd3\x0f\xb3\xf9\xc1;q\x9c,M\x93:\x93\x0e\\V\xba\xad\x13\t\xe04\xb7i8\x11z\x12U\xb80ZkJ\xa3\u00f6sO\rve\\'\xfe&\x8f\xbf\xaa\xc5\xfc\xf9\xeeQ\xb1bBmh\xf6\xfc:\xf7L\xa6\xfeo\xb6o=\\{\xd7\xf5\x9fg\xf0\xc8~\x19\x9b\u0724\v\x9e\t\x03\x84\x90\u0338\xe0\n\xa6\x9e\xfb6\x02/\xb4\x129O\xa3\n>\xaa\xe0#\xcba\xc5\xc1-R]\xd8\x01\x01\x8d\bmjI\x1c\x95m\xa4\xc2\b\xab-\xb4\x8bP\xe0(\x81\x02L\u0672\xf6\xa2\xc1\xb1\xba;\xac\x1dg)\xf0Q\x8e\xc3\xd4\x15g\u04f1p\x15\x87\xb7l`\xd7S\x0f\xb3\xf7\u0675\x1c\u06f3\x15o\xb8\x9f\xed\x8f\xff\x8a\x1dO<\xc8#\x13;\x99\xbap9s\x17-b\xf9\xc2\xf9\x9c\xbal!\x8b\xe6\xcc`\\mM\xbaW\xc2\x18\x9d\x04V\xc4\xef\xcao\xaby\xa2*\xd8VDV\xb8\x95\x93K\x00\xec9\xd6\xc7\xc6-;xi\xe3v6m\xda\xc2\xd6M/\xb0o\xfbF\xcaC=\x00\xd46\x8dc\xf2\xf2\xf3\x99\xb6\xe8\\\xa6\xcc?\x8b\xa6\xc6NB?\xa0T\x18\u0184a\x92rb\x17j\xd4%%\xfffC\x1c\x05\xa0B\x83S2\x04\xa3\x86 \xa3Py\x97\xacR\xe8 \xd7\xd4\u01b4SWsp\u04f3\x8c\x1c?\xc2\u06a7\x9e\xe1mo\xbc\x88&G&\xb9\x89'\xafW\xbf+\x0ft\x88\xa3$;\xb6\xef`\xeb\x96-\u0535L\xa0}\xfeJF\xb5\xb5]\xad8\x01\xc6)C\"I\x90\x17\x81FZ'\x14\xf0\x02BW 4\u0220\xd26\xc6E\xca6O\x95\x82%\xa4c\xbd\u024d\u0104\xe6\x841\xf6\xb4\x8a\u00cc\xd5u\x18C\xc9\xf7h\xa9og\xc1\xd2\v\u0637k\x03\xc5r\x81L&\x97nJQN&\xc1\xbc\x83r\x81BO7G7=\xc9\xee_\xddJ\xcb\uc974/\\\xc5\xcc7\\I\xf3\xccEH7\x83r\xb3\x89\"\u013a\x12Z\x88VD>\xe3a\x10\x10\xf8^\x15!\x89\x90\x84\xdaP\f\f\x8e2\xb8\x91\xef\xad\x04\xbc\xd1!\x9c\\\r\xe7^\xfd)\x1a'vR\xd3\xd8\u00ac3.\"\xf4\xbcd\xe8'\x9em\x91\xe6D}y\xfc\x9a\xc4\x19\xaa\xb1\x90\u008c\x11\xbfHi\xdf\u02d1\x9e\xee\b\x86\x16),]RSS\u00e2EK^~\u054a\xf9\x86\xee\x11VL\xb0\x83A[&\\\xfc\x8e\xfdG\xcbk\xee\xfe\xb7\x1b\xd8\xf3\xe4\x03Q\xb1\x92U\t\xd3\xf1.>~\xc9\xe9\u033f\xe2\u00e8l\x8e``\x98l)D\x16}d)\x88r\x99t\xb2\x83\xc5\xc7\x11\x11\u0623`\x92I\xa8e\x1c\xe4Wy%\xc3Hq%\r2\x8e!)i\x8c\f\x10\xf5N\xb2\xad\x1ac5\xb0\xa1\uf8e4b\xda)g\u04f1\xe4T\x16\\\xf06\x0eo|\x9a}\u03ed\xe3\xe0KOS\x1a\x1ed\xe0\xf0>\xfa\x0f\xef\xe6\xc5_\xdd\xce=\xad\x13\x98:k\x1esf\xcfd\xe1\x9c\x19,\x9e;\x93\xe9\u04e60i\xe2\x04\u018dk\xaf2\xd8\xfa\xcf^~\xe8\xd1\xd5}\x8c\xae\xa3\xc7y\xb9\xab\x8b]{\xf6\xb1}\xd7\x1ev\xef\xdd\xcf\xce\xed\xdb\xe8\u06b7\x1b\x82\xb2]\xe8\xcae\u0714\xb9L]v\x1e\x93\xe6\x9e\u0284)K\xa8k\x9c\x84\xf6|\xca\xc5\x02\x84:\xc9f\xac\xe0Yc\b\x9a\u06379\x9a\xed\x16Q\xfa\xba\xe3\t(J\u00bc\xc4S\x90-\x97\xa9\xa9\xabc\xfa\xa9\xaby\xfaG\u07e4<:\u0313\x8f<\xc8\xf3W^\xc5\u014b\xa6a\xbc\xc2\xefl\\v\xf2\xfa\xdd/?\x88B\x8d\xb5\u6847\x1f\xa6\xeb\xc8\x11&/?\x97\xe6\xd9K)\x95<\b-\xf6-\f\x89\xef82vB\x8c\xd1Z\x89\x8a\xf8\x13U\xb2\u0713\x8cn9\x13y \xe98\x861n\xaa\"\x0f\x13\xa52\x18\x13F\xa4\xa3\x8eh\u0394@\xbd\u2ddb@1q\xeeg\xa85\x05\xcfg\xfa\x82s\x98\xdcy\x0f;\xb7?m\vo\x94\x04\x11\xdb\xef\x1a\xa5\x10FYO\u01e8\u0287^\x89\xee\x97\xd6\u047b\xebE\xba^Z\xcf\xf8%g\u04b1\xeaB\xc6\xcd_\x89r3\x98\xa0\f~\x19\xc2 \xf1^\x89\xa3\xe5D\xe4\xefbR)E\x00\xe5\xd0\xe0\x866\xffS\x1a\x83\x89\x14*~\xb9H\xeb\xd4Y\x9c\xff\xe1\xbfKN\r\xa1\xefY\u062a*\x9c\xe27\x9d\xabM\u22de\xda\xe7*\xc0DdC,$\x04^\x89\x81\xeeC\x89 \u013e\x01\x9a\\\xae\x86q\xe3\u0195O9e\xf9\xab\u04d9\x1f\x19\x1c\xe1\xd1cQ\xd3\uc3f6=Q\xe0\x8bk\ufedb\xb5\xb7}+*\x1a\u056ezB\b\xc2\xc0\xa7v\xdcD\x96\xbc\xfb\xafh\x9d4\x8f\xf0\xd80\xd9Q\x0f\x15\x00\x81\x8e<\xb2M%4V\u0605)=[\xc8\x11\xa6\xc2\x06\x1bK\xc4\bm\xc7d\x8d\xb4/\xa4\xd4 C\x99\xec\x84B\x02%\x03\x8e\x86\x1aY\x95\xb8\r\x10\xe8\x90`h\x10\xa9$\x93\xe6-e\xe2\x9c\xc5\xcc=\xf7\xcdt\xef\xdc\xc4\xe1\xcd\x1b8\xb8\xf1i\xfa\x0e\xed\xc3\x1b\x1d\xa1\xd8{\x8cm\xbd]l{\xfa\x11~\x8e\xa0\xb9m\x1c\xe3\u01f5\xd1\xda\xd2D[[\x1b\xadmm475\xd1\xda\xdaB}}=\xd9\\\x0e\xd7\xcdD\x836v\xc4\u0646\b\x94\xe9\xeb\x1f\xe0\xe8\xb1\xe3\x1c;~\x9c\xe3=\xbd\xf4\xf5\xf7\xd3\xd7\xdb\xc7\xf1\xa3]h\xbf\x92\t\x9a\xcd\xd5R\xd36\x81q\xd3\x17\xd11\xff,\xc6O]B\xcbf\xa3\xc1\x82\x00\x00 \x00IDAT\xa4\x99d\xf2\x8d\xe8\xd1\x12\xa5\xc1A\x9b\xcen\u0184\x0f\x881\x06\x9ecL\xf8e\xf4\x9e\x18$Z+T\xe8\x90)I\xfc>\x8d_+p\xb3\x06\x9c\x80\t\xb3\x171u\xd9\xe9\xec\\\xff\x00;\x9f{\x8aG\x9e\xda\xc0\xf2\xf9\xd3hS\x12\x1d\xe8\x93\xc3C\xafvW\x1e\x84(\xc7\xe1\xc0\xfe\x03<\xb1~=\x81PLYt\x06\x8e\xd3@a\xb8\x80\x0el\xc7h\vz\xa5\xa0\x1at\xe4\xa9#PH\xa4\x0fF\x9a\x8a\xe2BG2;i\xd0J`\x14\x84R \x8d@\x06\xd1\xf4\xa7\x14\b\xe1 \x8dk!\xbctdb<\xb6n*^\xe3\xe9\x8f\u0152A/\b\xc8\u05f6\xb1h\u0645\x1c\u073f\t\xdf\xf7q3\u0654\xe5m\xf5\xf0LlK\x81\x90(7\x8b\x0e\x02z\xb6=G\xcf\xf6\xe79\xb8\xf6n&,9\x93)g\xbc\x81\xc9\xcb\xce\"_\u05c8\x0e}\xc2R\x11\x13\x06\xf60.L\x14\xe5f\xed~U\xd4\xc8H \xc4P\xf45\xae\x14d\x85\x8c\x8c\xe4l\xf3\xe9\xfb\x1e*\u04b2\x1bm3\v\xd2F\x06R\x8a\u02ae0F\xfd\x93D\xdcE\xbfK\xaa\x05\x8b\xb1%\xc9\xf0\xf1.\x86\x8e\x1dI5\xbe\xf6g\xad\xa9\xc9\xd3\xd1\xd1\xf1Rg\xe7\xf4\x97\x00v\xed\xda\xf1\xca\x16\xf3m\x83\x88\xabf[M\xf9N\xa7\xe6\xcf7<\xb7\xb9\xe3\xe7\xdf\xf82\xde\xc8P\xc5C8\xfd\x86\xea\x10\xe5\xb8,^\xf3G\xccZ\xb4\x06ul\x14Y('\fzl\xfc\x9b(-\x05\x88\xd0 }\x1d\r3\xa4X\xe3d\x81\x88\xca\xc8l\xec\x02d\"\xf1\x94\xa8\u021a\x84\x06S\b\x11\n\xc8\xc9\xea\xe2\x86\u0571k\xad)\x0e\r\x80\x10\u0536\xb63\xe7\xac7\u0439\xfc\f\x96\r\xbe\x97\xfe\xc3\xfb9\xb2\xfdE\xbavl\xa2g\xff.J\xc3\xfdx\x85\x11\xfa{\x8e\xd1\xdfs\xec\x84\xd7F\np3\x19\x1c\u01f58c\x14{d\xb4&\fmZJ\xf9\xb7\x848g\xf3ud\x9b\u06e8in\xa7m\xd6b\xc6\xcdXF\xfb\xe4\xf9\xd47M\"\x9fkB\xe1\xe0\x97\n\x94z{\x11Z$\x1d\x80\xd1&\x05\xab\x90\u023a\xaa\xe2\x1e\x93p]S!\x92\xa5\x02\xe1 \xb4\x83(\x83\xf2B\xf0\fA\x9d\xa4\xe4\r\xd3\xd8:\x89\xb9\u7f45\x9d\xeb\x1f\xc0xE\x1e\xfe\xe5=\x9c\u007f\xe1\xc5\\<\xbd\x11',\xe0\x9f\xac\xe6\xaf\xf8%\x84\xf5W\xf1\xfd\x900\f\xc9fs<\xf2\u0223l\u077a\x95\xd6\tS\xe9\x98}\x06z$\x80r`\x15\xbb&\x1d*n\u05de\x8c\xd15%\x90Q\x97NhR\x1dqtOi\x8b\xf7j!\b]\x81\xd1\x12\xa1\xb5\xbdO\x8dU\xb6\b\xe5 B\x1f\xa3\xd3\x16\xafT\x85>\x8b\xb4{a\xfcA#\b\x82\x10\xdfHf.8\x8b\xc9\xcf\xfe\x82}\xfb\xb6\u0635+\u0485<\xa5\xd1\x16\"j\xdc*\xae\x83\"\"n\x87\xbb\xf63\xd2}\x88\xae\x17\x1fg\u0082UL;\xeb\x8dLZ~.\xf9\xc6\x16k\xd0U*\xe2\b\xaa\xf0nA\xf5p\xa0\u0586R\xa0q\x94\xc0!\n\u0280$pF\x18\x91\xd8\xf1\xc6E\u061e$*\x83C\x82\x94\xbdA\xb2a\x9c\xe8\xfd\x14\x8d:\xa2\x94B\n\xe8\xda\xfe\x12\xc3}\u01d3\xcd\xc1\xdapHr\xb9\x1cs\xe6\xcc~\"\xfe\xfff\u03de\xfb\xca\x14\xf3\x83\x03#t6\xd5qag]T[\xcd\xe2\u06f7\xf7|\xfa\xc7_\xbf\x8e\u00db7\xfcFxE\nI\x18\x86\xcc8e\rK.\xf8cra\x0eot\xb8\xc2\xfe\xc6$\x89\xa80\xc32\xb4\x8c\xbc\b+\v\x04\x93\xda\xe5\xa9\x18\xea\x98\x04\x96\x13\bi\x1d\xc9|$\x19a\x902\xaa\xae\x010\xaa\xed\xa0\x84\x9bJ\x1eO0\xb0\x94\x13b\xb1@ \x04N&K\xcb\xe4\x19\xb4t\xcc`\ua2b3\xf0J%\x06{\x8fs\xec\xc0^\xfa\x0e\xef\xa6\xff\xe0\x1e\x06\x8f\x1e\xa68\xd4Oyx\x00\xbf8\x82_*\xe1{%\u029eO\xb9<:\xf6\xb6\x04\xe5\"3YrMM\xb8\xb9<\x99|-\x99\xdazr\r\xcd\u050f\xef\xa4y\xea\\\x1a\xa7\u03a6~B'\u0646\x16\x1c\x95%\xeb)\x18\xf5\u0445\x02a\xb9h\x8f\xd2(\xabJ1\xa6z\xdb7\xa6\x1a\x97\xa7\xe2\x05m\xa8V\x04\t\xa9\x90\xd2E\xaa\f\x02\x05\x81]n\xca\xd8\x00g?\xab\xc9\xe7\x05S\x97\x9d\xcf\xc4\x05\xcb\xe9\xda\xfa\x02/<\xf4\v\x1eZ\xff^fO^C\xa7\x94\xc8\xc87\xfb\xe4\xf5\xca]\xf6T\x17R\xf6|\x94r8p\xe0\x00\xf7\xdd\xff+\x06FF8\xfd\x9c\xb3hn\x9eN\xe0\x85\t\xe9Y9\x8e\x99\xe4\x18&\x8c\xb4]v\xe4RJ\xc5f%)\xc2I\x90\xb1 \x95\x9c\x1c\xed\xf9\xc2rUV\x8b\xa0,, C\xdb\u045b\xf4]\xa9\xab\x03\x89\xc6\xe8\xd6\rP\xf4<\x9a\x9a:X\xba\xf2M\x1c9\xb2\x97 \bP\u02a9\x10\x86\xa6\x02\x87\x86a\x88\x10hG9\"\fC\xa1\x03\x9f0\x88\f\xf7\xa4-q\xc3\xdd\a\x19=v\x84\x97_x\x9c\x89KNg\xfe\x9b\xaeb\u0082Udkj\xc0+B\x18\xa2\xa4\x8d\x88\u04c0\x17\xa6\x03\x9e\x05\xa1\xb1\x01\x1d\xb5\xd2$\xd8u\x1c\u0111l\x88\xa2\x12\xa5\x97\x14u\xaa+\xb6\x10)\x188\xc5\x05\x1b\u049b\x1cH%\t|\u0631\xeeW\x84QS'\x85\x04cp3\x19:;;\x99>}\xfa\xa3\xe95\xf0\x8a\x14\xf3\u03a6:\x8e\x8c\x96\x99Tk\xa7\x0e\x9f\x1d\xe5\v\xbf\xba\xeb\x8e\xdc\x13w\xdc\x12U\xee\x8a\x13\xa2\x05\xfa\x15:\xf4i\x9d<\x9b\x15o\xf9\v\x1aZ;)\x8e\f&8\x9a\x88\xbc\x1eLD\x02\x1a\xa1\x11\x81%d\x84\xa6\x92p\x93\xb2\xbe5b\xcc\xd1&\x15\xfddM\xf25^`\xacw\xb1\x93\xac*K\x88\x8e\x82hP\x95x\uf60d7\xa6*B\xca\x18CP.\x13D\x13\xa9\xc2qqs5\xb4M\x99N\xd3\xe4\xe9hs\x01A\xa8\x19-\x96\x19\x1d\x1a\xa0\xd0\u007f\x9c\xd2@\x0f\xe5\xe1~\xbc\xd1a\x82b\x81\xc0+\xa0\x83H\x9b+%N&\x8b\x93\xad\xc1\xa9\xa9#[\xd7H\xa6\xb6\x9elC3\xb9\xa66r\xf5M(7WI\x8f\x05t\x10\x12\x84>~P\xc0\xf1}T\xa0\x13\x85B%p0-\x1c\x88\x19\xf6\xf8\x06\x16iuY\nw\xb27\x82\x90\x0eB\xb9v\x80+\xe9H\xc0\x04\x91\xb1\xbf\x94\x94\x06\x86\x187~.s\xcfz\v][_\xc0\x1b\xee\xe3\xa1;o\xe3\x8cs\u03a5\xa5#G\x9d(\x9c,\xe6\xafpW\x1e\x04\x9a\xb2\xe7\xe3\a\x01\x99l\x96G\x1f[\u02f3\x1b6\xd0\xdc6\x89\x99K.@\xca,Aa4\x9daV\x89o0\x15\xcc\xda\xe2\xdb\xf2\x04\xf5\x85 \x95t\x9f\x92\u0449\xd0~\xaa\xebH\x9c\x88 \xb1\x84\xaa\"t\x1ckb\x15\xc1\xa1\x15\xb5Fe\u011e(\xad\u0224<\x94$\x06?\xf0(gj\xe9\x9c}\x1a\xe3\xda\xef\xe6\u0211\xbdQ-\x90\x91\u72c98\"I]}\xab)\x16Kr\xa0\xf78H\x11*\x89\xc4\x18\xa1CKrZ\u0264\v\x02\x8a\x83\xbd\xec[w/=;^b\xc69\x970\xef\xc2\u02d80s.\x19GQ\x1a\x1d\u0173)\x17d\x94\xed\x9eCc\vy`\x04~\xe4c\x93\u02d0H\x18IQr2vG\x15\xe2D\xd1NU\xf1Oo`c\x06\xfd\xa3\u04f1r\x04\xc7\xf7\xecd\xdfs\xeb\x00R\xf9\x9f\x06\xd7\xcd\xd0\xd9\xd9\xd9=y\xf2\xe4g\x01\xae\xbb\xeeZ>\xfe\xf1O\xbc2\xc5<\f\n(\xc7\x16\xf2\x97\x8d\xf9\xd8m\xf7={\xd9\xdd\u07f9\xde\xc6s\x83\x90c\x16\x8a\xd6!\u065a\x06\x96\xbd\xf1O\xe9\\|>^i$\xe5\xbef\a\x19b\xa3,!%\xb2,\xec\x00C*\x930\xc9\xeeHgIUy\nW\xceR\u0080\t\xad\x87\x84\x0e\xac\x86\xb5*\xe2{Tc\x14\x88\xdaH\xab\xae\x89\xa4V\xd5T\x86I\x919Bk\xf0\xca@9\xe9j\xa5T\xb8B\x91\xcfg!3\x81|\xdb\xe4J\x8cZ<( \u04a9%\"\xbe\x13\"c\xba\xf8\xe8\xa8#\x9bQC\xa8C\x1b^\x1b\xb9\xd4\xc5x\xa1q\x05\"\xa7\x90e\x93\xe8\xea\x05\xd5\x1dy\u0711I!\x93\x9b\u0264N=\t\xeb.\x94\xbd\x01\xa4\x8bRN\xe4_a\xaa\xa6\x04\x85\x89\n\xbag\bU@\xb6\xd6a\xf6\xca7\xb1\xf9\xc1\xdb\xe9\u0677\x9d\xadk\xef\u7a67\x9faJ\xeb\xb9,\xccJ\x94\xd0'\xc9\xd0W\xa4\x90\v;%\xe9\xfbx\x9eG6\x9b\xe3\xf0\xe1\xc3\xdc{\xdf\xfd\xf4\x0f\f\xb0\xea\xfc7\xd02y>\xa1\x1fB\xa0#\xbf\"\x12\x88\xc3\xca\f#\x92\x1b\xab\x9a\x12B\xd9\xf7WX\xb23N|\u0462:?GG\xd0BNJ\xb2Y+$ 4\x18\x19}L\xabJb\f\xf1\xa8\xe9\x18\x9f\x12\x93Z\xfb\xa9/n\xb4\xa1P*Q\xdf<\x85y\v\u03a4\xbbk_J\x9e\x17Y\x0fD\x039\xabW\x9f+::\xa7\xbd\xfc\xc2\u018d\x93w\xef\u06a9\xf6\xef\xdcn\ub0b4~&:4\x11\x89j\u05f5\x00\x86\x8f\x1eb\xe3\u03feC\xf7\xc6\xf5,\xb8\xf0r\xe6\x9f\u007f\tM\x13;\x19-\x96(\x95\xcbv\x03J\fj+\x9a\x1b/4d\xb4\xc1Q\x95\xe7\x11\xf3\f1\xf9\x99\xa44\xa5\x92\x8a\xe2B\x9cL\xacF7\xa5L\v\u0222_\xa5P\xb8\x19\xd8x\u07cf9\xbe\xdfz\x1eIe}\u0305\x90477\xd1\xd8\xd8p\xeb\u99dfy\x04\xe0\xf2\xcb/}\xe5\x8a\xf9\v}B\x02\xda\x18s\xfa\xdd;\xfb\xfe\xe9\xce\xef|\x95c{\xb7G\xb4\xb6\xb5r\x14VRb\x85\xf5\x02f\x9dv\t\v\u03bb\xca\x16Y/\x88\n\x87\xb1\xb0J$\x19\x14\xda \x03\x81\xf4\x05\xf8Q\xdd3q\xe7]\tI\x8d\xb3\a\x8d\xa9:\xc0$\xb8[\x14\xa7\x12\xad+\x83\x0e\xc2h\x047\xeaV}\"\x8a\xdbA\xd4\xc8\x13:r\xfb\xb5S\xbe\xb81\xfb,\xd2\u040e\x86P\xa3M\x00\x06B\xdf\xee\xee1\xd6fDu\xa3R\x95P\x12\x1dI\rc&X\u0349\x98\xb7I\x1d_\x83\xacDf\xa5\xdd\xe8b2\xcaT\ba#H\x05\xf6\x926g\xab\u0605FS\xb8\u05ab\u0669$\xb1\x8c\x91\xc0\xc6I\xe4\u06b7)\xbd\xe5\xa1a\xa6\xccX\xc1\xfc3/\xe1\xf1}\xdb)\xf4v\xf3\xf0m7\xb1h\xe5\u9d0c\xcf2\xc5-\x9e\xb4\xc4}\x85\xf0\x95 \b\xf1\xfc\xc0F\x9f)\xc9#\x8f<\xc6\xfa\xf5\xebii\x9b\xc8\xecS\u0788r\xeb(\x15KQg\x1b\xdd\x1f\xa6\u04bf\xc4P\x8a\x10\n%\xdd$\x872>\xb9\x19a\tO-\xa5%M\xa3\xc9F\xa3\xb0\u0120\x13\xf1|\x0e\xa9p\xe2\xc8$/\xe6\u0174\xb40\xa6\xd1T\a\x8f&r\x96d\xb6!n@\xfc\xc0C\xe4\x1b\u9735\x92\x86g\xefchx \xcab\x96\x897\xb8\xef{\x94\n\x05\xae|\xd7;~\xfa\xa1\x0f\xff\xd9\xfao|\xe3;\x97n\u06fa\xe5\x8a\xed\x9b7\xe5\x8e\x1e>@\xe0{\x91\xaa&\x88\x1cU\xa3\xcdJJ0\x9a\xae\x1d/\u0473\u007f'\x877>\u0242\v/\xa3s\xe5j\xea\x9b\xda(\x97J\xf8\xbe\aZ#\x05\xf8\xa1\x85H\xe2\x00x7\n_Q2.\xe4\xd2n\x84\xca\xde?FV\":D\x95\u0657\x957J\x11)i\xc6\xeem\xc6P\xd3\xe0\u0435}'\xcf\xff\xe2G\x16>q\x1c\x94\xb4V\xbeJI\xa6M\x9b\x1e.[\xb6\xec\x81\xf8\xff\x99>}\x96=\xa5\xfc\xa1\xd7\u05be\x82\x91\v\x9b\\\r\xf0\x96\xff\xf5\xf9/\xfd\xf0\xdb\xdf9\xe5\xeeo|9\x04T\xea<E\xack5:\xa4c\xe1\x19\x9cs\xd5\xe7ih\x9d\x867:R\x91\xebD\xd2'\xa1\x05\"4(_\xa3|PZ\xa4 \x841\x05\x8e\xb1\x12\xbbT\xe7\x9b\xcasE\xa4vTc\xacdQ\u01dd\xbe\x01?\xfa\xb7\xacD\xb8c\xb2\x00\x93\xef'*P\x10\x8c\x89\xa2\xaa|\xcc\x0f\r\xe5\xd0$2\xb0\x18\xb2\x11Qwm}\x8au\xd2m\xa7C\x91\x93\x82m*\xf7@|<\xad\xcal\x8c\xb0K-,\xfc\x14\x0fx\xa4\xd5`UZ\u05b4\x061\n\xb3\x95\xd2A\xban2)g\x17\xfc\tMT\xa2vI\x9e\x8b\x10(i\xc8\xd5\xe4\xc9\xd67s`\xcbz\n\x03=\f\x1e\xef\xa2q\xea<\xda\xe7\u0367Y\x84\xd4:\xe6$\xdc\xf2\a\x86WB\xad){\x1e\xbe\x1f\x90\xaf\xa9a\u03de\xbd\\\u007f\xfd\r\xec\u0739\x93\xa5g]\u01bcS/'\u0502 \xb6j0\x11\xb4\x96\xea\uc171\xd3\xd7\xcaq\x91\xcaI\xab\xe8l\xef\xa3\x04ZEo\xba\x02\xa3$AFb\\A\xad#\xc8\xc4\x1f\x13\"\x82\xf8\"\a\xc6P\xa3uh\xbb\xe3\xf8\x84\x9d$\x17\x8f9\xe9\x91\xea\u0423nAG$|]\xae\x86\xfe\xa3\xbb\xe9:\xb2\a%]\vgD'\\)\x04\xa3\x85\x02\x8e#7\xbf\xfb\xb2K\xbf\xf4\u033a\xc7~v\xedw\u007f\xf0\x8c\x10H\xc7\xcd,,\x95\u02b2\\\xb6\xf6\xb9\xb6V\x1aat\x98</k-\x1b\xd2s`7\x876=M\xef\x81]()i\x9a<\x8d\x9a\x86&\xeb4*\r*^\xf4Ql\x9bR\x8a\x8c\xe3\xa0\x1c\x85r\x14\x8e\xe3\xa4|\u0355-\xda\"M\u041a\xe4\xf9*Qi\xae\u0318\x8d9[\x93\xc7\x04p\xdfu\x9ff\xfb\xda\xfb\x91R\xdab\xae\x14\x80inn\x16\x17^x\xc1\x86O~\xf2S\xd7^s\xcd5\x85\xad[7\x89\xaf}\xed\x1b\x89\"\xe6\x0fzm\xef\x1eT\x00\xbd\xc6\\\xb5\xee\xa1\xf5W\xfd\xfc\xa6\x1b\xad2u\xec\x14d\xe4\x02\xd68\xbe\x93\xb3\xdf\xf1\xb7\x8c\xefX\x8272\x12\x8d\xf6F\xc56\x04\xe5\x19T9\xc4-j\x94\x17\x17)\x81\x92.*\xc2r\x11\"96&\xaf\xb9\x10\x91\u02df\xf8\r+\xa6R\x95uh\u0426\xf2A\x11R\xc1\x15K\x1a=\x18`\x02\x92\x01\xa4\x8a\x06\xfb\u0109\xaex9V\n\xb1\x1dN\b\xb4FkK\x18&\x8c\xbb\xae\xb8\u02c9\x14\xbc]\U00069c0fP[\x88\xc5FhE\x12*c\xd9\xf6*\xd7\xc6\xc8HH\xbb\x02\xbfF\x1286n+\x8c\x8e\xbcUi(\"\xea\xab\x13\r\xa2\x1d\u00d6\x8e-\xe4(\u01e6\x80\x13\x1d\x99SG\xc1\x84\x9fH\x9fTB\x83\xf6\xc1\x1b\x19a\xe2\xf4\xa5,^\xfd.\x84\x14\x8c\xf4\x1e\xe3\xd1[\xbf\u0266\xdd\xdd\xec\xf7s\x94C\x13\xb90\x9e\xbc\xfe0\xf0\x8a\xa1\xec\xf9x~@&\x9b\xc1\xf3<\xee\xbe\xfb\x17<\xf3\xcc3\xb4O\x9a\xc9\u0715o\xc6\xc9\xd5\xe3ye\x12\xec.nXte`G`\xb3>e\xe4\x8bdHd\x1d\x95H\xb9\xd4\xfd\xab\x1d(\xd7Jd[\x86\\s\x06\x91\x89&4\x1da\xff\xec\xdaiN#\x04B\xc9\xc4J\xc3(\xecC$\x06\xa6\x89-E\xfc\u0426\xb2\u07a5\x90\x94\xbc2\xd9\xc6\tt\xce\\N\xc6\xcd\u0606H\xa4\bF)\x18\x18\xe8\xe7\xf8\xb1\xe3o\u0771s\xd7B\x80\xb7\x9fs\xca\x03\xdf\xfe\xca?~\xe0\xfd\u007f|\xf5\x9b\xdey\xd5\xfb\u007f\xb5\xf2\u0333i\x9d0\t\xa4\x93\x0e\xdb\u0104!\xa1_\xb6\x1e\xe9\x02F\xfbz\xd8\xf2\xe0\x1d\xfc\xfa\xfa\xbf\xe5\xa1\x1b>\xcd\xd1-\xcf\xd0\xd0PO\u02f8\xf146\u0593\xc9f\xc1\xcdbT\x862\n\xed\xb8\b7\x13=\\\x90\n\x13E]'gkQ\x11p8R$J\xc5Tnv\xd2df\xf2y\\\a\x1e\xbb\xe9Z\x9e\xfa\xc9wm\xb7\xed8\x11\xf4\x85\tC-\x16-Z\u013cys~$\x848>n\\\x9bZ\xb0`qr;\xfdA;\xf3\xa7\x0e\r\x88\xf3\xa75\x85\u0198\xa6\xfb\xb7t\xfd\xeb\xbf]\xfbOSv<\xfdh(b\x11uJEa\x8c\xc6q\xb3\x9c\xf5\u058f\xb3\xe8\xac+\xf1K%K\x04\xc6<\x896\xa8\xc0X\x82E\xa7\x18\xe0\x8a\x99\x83=.\xc5\x1dA\xfc\xfbX\xe8\xa3J\xd5)\xaa\x8c\xf1\x13T#6\xaf7\xd5\x04\x86\x9d\x8c0V\u0652U\xd5#\ufc5f\x82H\xdd\x14\xa9\x82\x19w\u035e\x86b\x90\xf2\xd8I\x9f\x0e\x12\x83|\xfb\xd6[\xa2h\f\xfcq\xc2\xc8/'<\u007f!R7\x9c\x10v\x98\xc3\u0122\x01\x11I\xc6R\xafQ,\x9b\x8a'\xf6\x1c\x17\x91\u0260\x1c\xbb \xb5J\xe9cS\xafk,\xbdJ`\x19Y\xbdO\n@e2\xb4u\xcc\xe0\xe5\x9d\x1b\x188z\x88\x81#\a\xc9\xd471a\xf99\u4961\xd5\xd5'\x90k'\xaf\xffb!\x8f\u0580\xe7\x05\x94\xca\x1e\xc6@]m\x1dO=\xf54\xd7^\xf7\xaf\xf4\xf5\xf5\xb1\xf2\xfc\xab\x98\xb9\xfc\u0354\u02be\u0748\xe3\xb5l*\x837\x96l\x94\b\u9814\x9b\x14\xf3*\xf6.\uaa0d\xac,\xdc\xc0\x11\x98zEc\xa3C\xaeVAF\x8e\xa9Pv\xdd\xfb\xc6j\xd75\x1a\x1d\xf9$Y\x8bQ]M\x10\xa61\xe34Y\x18\xe1\xcc\xca\u0252s$G\x0fm\xa1\xbf\xefh\xc4\xe3Tn\n\xad\r\x19\u05eds\x1c\xe7\xc1\xfb\xef\xbbo;\xb8\xea\x9ak>\xa7\xef\xbd\xf3\xa7{\x9f]\xbf\xf6\xd6\xfaq\x13\x0f\xb8\x8eZ822\xd2R,\x96D\xe0\x95\x01aD\n\xff0QL\xa5T\x8a\xd2\xf0 \xc7vm\xe2\xd0KO\xe1\x15Gi\x9e4\x95|\xdbxL\xb6\x16\xa3\r\xa1\x94h\xe5D\u0779\x8ax\xaf\xd4t\xa6\x00W\u0187^\x81\x12\x16\xeb\xafVb\xa7g_\x05\xf9\x86\x1cJ\xc0\xba\u007f\xff&\xf7|\xe5o\t}\x1f\xc7q\xad@CJ\x82 \xd4\x13&L\x90+V\x9c\xf2\xe0\xe7>\xf7\xf9O]s\xcd5\u07bd\xf7\xfeB\xde|\xf3\xf7\x93r\xf0\a\xed\xccO\x9f\xd2d\x00^\x1c\xe1\xf2\xfb~\xfc\x833\xd6\xdf\xf5C\xbba\x981\x9dlT|\xe7\xad|+K\xcfy?B+t\u0673\x9a\xf1\xd0 \x83\xa8\x90k\x93\x14\xf2j\xb2\xa4\x82\xb5\xc9(\xe8U\xa9\fJ:Q\x81\x97\x95\x02O\xcac2)\xba\xd5\xf5D\x87\x10\x84\xb1A\x96H&\xe0@@ \xa0`\xc1-\xa1RE0\xaadf\xccfB\x14\xf6j\xb0\u01f1b\b^\x84\xd1K\x91\x86\xbe-\xf9\x14K\x9f\xd2A\xe6c1\xed\u050cDu!\x17\x15hE\xa7:h-\x05~N\x11\xba\x12\xa3\x04F\u026abo\x84\x04\xa5\xec\xa8s&\x87\xccd\x11n\x06\xa3\x94u\xcfs\x05AF\x10:\x820\xfa\xbbQ\u0092a\xa9<\xd3D\xd2(m^d\xe0\x19\xc2b\x89\xd6\t38\xe3\x1d\x1f#\xdf\xd0L\x18\xf8\xac\xbd\xf5\x9b\xac\u007f\xf8!\xb6\x15\xb3\xbc<\xaa\x91\x9ctT\xfc\xbd`r\xb0\x83e\xe52A\x10\x92\xc9dx\xf9\xe5#|\xeb\xdb7\xb1s\xc7\x0ef,<\x93\x85g\xbe\x03T\x8e \x8c\"\u03f5\x1d\x96\x8bo\x85\x8a<N #\x99pZ\xfcDJ|\"\xb0\xf7\xa5\b\xa3\xf5\xe5J\xea\xf2\x8aZ'\xb2\xe2\xa8U\xc8V\x17\xd1\xea\"\xea\x1d\u00ac$pA\xe4\x14\"\xe3\"\x1d\aGH\x1c)P\xaab\xdf!RSj&u_\xa4\xbe=BH\x8a\xe5\x12-\x13\xe7\xd19c\x99\xdd\xc8t\n\xeb\xc4\x12\xc0{\xf6\xec\xe5\xe0\xc1\x83W\x1bc\xf2\xe0\x87\x1b6o\x8b\x90O\xa1\xdfs\u025a\xef\xdd\xf4\xf5\xaf\xce~\xff\a\xde\xff\xe9\x8b.\xbe\xa8w\xe6\xdcy\xe4jj\x84\x91J;n\xc6\xc88cX\x87\x84\xbeg\u036d\x82\x80c{\xb7\xf1\xf07\xfe\x81\x1f}\xea}l\xb8\xfd&\x8a\xbd\xdd\xe4\xeaj\xa9\xc9\xd7 \x8dI\u049at4\xc3\x11\xd1T\xa8\xf4\xcfV\xf53\x99\xea|\xd4\xe8\xf5\xadk\xc8\x12\x16}\x1e\xfe\xc6\x17\xb9\xf3\v\x1f\xc3/\x15\xadE\xb7\x92\x91\x8d\x87\tkkk\u054a\x15+JW\\q\xf9\x17\x85\x10#\x9f\xfb\xdcg\x9c\u056b/\xa8J\x81\xf9\x83u\xe6?{\xf4)\xf1\u36ff\x8b1\xa6\xf9;\u07ff\xed[\xff~\xe3\xb5\xe3G\a\xfb\"VWU\xfc\u0123\xe3{{\xc7\x02V\xbf\xe33\xb4N\x9cK\xb90RU\u016a\xcc\u0628\x16j\x8eEJ\xa2\xe5\x98\n~\xae\xd6|\xa6\xf1\xf9\xaaH\x8f\xd4G\xe3\x89+G\t\x94\xac\xdep\xec\xc6\x00\"\xefX\xfc<Z\xfc\x89\xbc2\xb50\x85\x8c\xe7\xc6\xec\xdfK!\x14\x02S\xe5A\xa1\x11\t\xb9\xa9\xa9\xf8V\x98\u07d0\xf0md\xe5y\x8b1I\xe0\xc2\xd8\xc9;\x11O\xf2\x85\x06\x19\x82\n@z\x06\xe9[O\x15\x15\x18\xa4\xb6\xa7\x0e)\x04\"\x92i)\xc7E\xb9\x99\xc8\x01\xb12\rk\xe2\xae\\F\x18\xa7\x14\x15eL\xf4}\xd3oB\xb4\xb7%c\xd1\x12;\xd4\xd0<y\x16}\xc7\xf6\u0475\xf3E\xca#\x83\x8c\xf6\xf72y\xc5yd\x1b[h\x91ej]y\x12?\xff\x1d:r\xb0\xa9\uc972\x87\x1f\x84(G!\xa5\xe4\xfb7\xff\x80\x9b\xbf\u007f\v5\xf5-\x9c}\xc9_0~\xe6\xa9\x14\n\xc3\xf6\x84\x1a\x91\x96\x95[\xa6\x92\x8b)\xa5\x83\x94c\x93qRS\x8a2\x82K\xe2\x10f\x05\xb2\xc1v\xe5\x99\xe8\xc0\x1d\xf352+!'m\x03 \xc0\x91\x96 U\u00a0\x84FI\x83\x02;\x89Le\x88M\xa4\xe4\x8a\xe2\x84S\xb6=\xc5g\xf3\r\x84\x85>^>\xf0\x12\xa5r!\"\x1c+\xcfW\xeb\x90q\xe3\xda\xe6ds\xd9;o\xbb\xf5\xb6\xaeo\u007f\xfdk\u0737\xf6\tf\u031e#\xd6>\xf4 \x00\x8f<\xf8\xe0\xba\xc7\x1ey\xf0\xa1\xc1\xa1a\x84\x10\x1d\x9e\xef\xd5\x0f\r\x8f\b!\xa4\x96R\x89\xc4\xdf)\xad\xd8\t\x03\x06\xba\x0er`\u00e3\xf4\xed\u07c9R\x0e\xf5\xcd-\u0536\xb4Y\x8f\x980\xc0\x11\x06%\xad\ubd8c\xeeSM\xac\xb2K\x87R\xa4\xd1\x02\x83r]j\x1b2\xf4\x1c8\xc4\xfd\xff\xfa\xf7\xfc\xea\x1b\xff\x94\x88C\\\u05cd\xe0\x15a\xc0\xc8\x05\v\xe6s\xf9\xe5\x97\xddt\xe5\x95\xef\xbd\x01\xe0\xfb\xdf\xff\x9e\xb9\xfe\xfa\x1b\xf8\x83\x17\xf3\xdbn\xbf\x9dw\xbey\r\x00SW\xad\xfe\xe0O\xbe\xf7\xed\xab\xb7?\xff\x94]6\xd1\x00B\xa50\n\xb2\xb9Z\xcex\xe3\u01d8\xb7\xf2R|\xafhM\xe4\xc7\xe4U\xff\xb6\xa3\xb8\xf8\x0f\x96z\xec\xbf\x1c\u007f\x9fd\xca,u\xb0I\x13\xa2U\r\xae\x10(!\x927C\b\x81P\")\xa2\xd2\x11\xd6\xcc[\xc9\u0502\x8b\xb5\xa5\x95i/\x1dg\x8dj\x18\xf6Lb\xfcobC\x9d\x94\x83\xa3\x11\x15V6\xfd\x86\x8b\u0602 *\xd020(/\xe2\x0f<\x83S68\xbe\xc1\xf1\f\xaa\xa4Qe\x8d*\x1b\\\u03e0J\xd1\xe7\xf8\x06\x11\n\xa4\xb6Gi),.\xaeT\x06\xa5\xa2\"\x1eMz\xa4\x9a\x1c[\xcc\xd3p\u0558l+a\xa8\xba\u046a\xce\xfd\x11,kB\x9fl\xbe\x9e\x86\x89\x9d\x1c\xde\xf5\f#=\xdd\xf4\x1c\xd8\x05\xd9<\x13\x96\x9dC]\u01a1=\xa3c>\xee\xe4\xf5_(\xe7\xbe\x1fR*\x95\xf1}\x9b\xa6S[[\u01e3\x8f>\xc6W\xfe\xe5Zzz\xfb8\xf5\xc2+Y|\xf6\xbb)\x95}\xc2\xd0J[e\\4\x93\x81\x16\xa2\xf0a\xebA.\x1d\xf7\x84BN\n7O\xbb\x8f\uab24\xa6-CM\xde\x0e\x18U\xa9\xac\x88< \\\x89\xcc)\x9c\xac\xb4\xc5\u0704\xa0\xc3\xd8\xf1?\x81\x1bBm\xc64h\xa2\xca\u0714\x14\x1c\xa9\x81\x86\xda\x1a\xba\xf6o\xe4\xf8\xb1CH\xa9PJ\xa5,\x024\xc5B\x91\xfa\xfa\xfa\x17\x1e_\xbb\xf69\x80/\xfe\xc3\xdfs\xd9[\xde\u00b57|\x95\xd0\xf3\u0637o\x1f\xd7^{]\xd7\xfau\xeb~q\xf3\xf7\xbe\xf3p\x10\x84\xadm\xad\xad\vF\x87\x87\xc5\xf0\xf0\xb0\xad\x1d\xc8\xdf\xe8r\x1axe\x8e\xef\xd9\u0281\xa7\x1f\xa0o\xffN\xbc\xe1Ajjkhnk\xa7\xb6>\x8b+\x9d\xe8\xa0,*[a\xea\xd4n\x930\xad\xeaE9.\xf9\xfa\fRJ\xb6>\xf2\x00w\xfd\xf3\xdf\xf0\xdc/\u007f\x9c|/\xa7\xba\x90\x8bi\u04e6\xb1f\xcd\xc5\xcf|\xfa\u04df\xf9\xe3k\xae\xb9\xa6x\xf3\xcd\xff&V\xaf\xbe\xf0\x84[\xe7\xf7\x96&~\xff\x96\xefs\xe5\x15W\xc4\xd8\xef\xacw~\xf0O\xfe\xfe\x99\xc7\xecN(\xe2\xc2\x1a\xb5\xdbR*\xc2\xc0c\xfa\x82\xf3\x98\xbf\xf2R\x00\xc2 H\x82\x9b\xd3\xed\xa90'\x1a\xe0S\xa1\v\u007f#\x8a\x98,Y\xa9P\xd1\xd1\xd1\xc6JE\t\x1dq{\xa1OTtTO\xa1\xa5gm#\u054dg\x10%\x83\xa8\xa9hD\xab\x95\"&\xd1Q\xfb\x1aF|\x83\x1f\rD\xe8\xcag\u06db)\xf6\xb9\x88d\x83\x96\x17\xa8\x98\x19\xc5$\x95\xd4$#\xd2\xc9\xe4^<\x82\x1d\x9b9\xe8\x8aBE\x88\xb8s\x16\t\x14$\xa5\xb4_\a\xfbzXZX$\x01\x1d:\xd6\xfd\xa6HRK2\xc9$?\u057a6ZH\xc5(\x13\x195\xa5<\x9aSu@\x1b\x83\xe7\x01\x03CL\xecX\xc4\xd9\xef\xf98w_\xf7Q\xcaCC<q\xdb7\x994g1\xad\xef|7\xadY\xc1\x94\xac\x1fC\xb9'\xaf\xff\xc4\x15\x04\x01\xa5R\t\xdf\xf7\t\xb5\xa1\xae\xbe\x8e\x1d;vr\xe3\x8d_g\u07fe\xbd\xccY\xba\x9a\xa5\xe7\\\x89\x91Y\x82p\xb4b\xb3:\xf6\x14\x9a\xbaO\xac~\xf9\xb7\x8bFM\x1a\x02\x14\xe0\xe6\x1459\x99D\bV\x11I\xba\xb2\a8\xae@d\x1ck\x8d\xa1\x02d\x18\xcdFDr<%@iC\xe0[\x05\x97L\xddK\xe9\x1e!\xfe\xd5\xf7}\xea\x9b:\x980y\x0e\xfbv\xbfH\x18\x068\x8e\x8a\x8a\xa6\x95g\x8e\x8e\x8e288\xf0>\xe0[\x00;w\xec\x00\xe0\x13\u007f\xf91^z\xe9\x05\x96.]\x9e\xfc\\\xabW_\xf4\xbc1\xe6\x03?\xfc\xe1\x0f\xee\x986\xb5\xf3Ov\xec\xd8q\xe1K/md`p0z\xbd\x94\x95-kS%C\x1e\x1d\xe8g\xf3\xaf~\u02ae\xb5\xbfd\xca\xe2UL]v&\xd3V\x9c\u0174\xa5\xabh\x9a\xd0n\xa5\x8ba5\xc1\x19\xf3ZRY\x88h\xa4o\x80\xfd\xcfmd\xf3Cw\xb3\xe1\x9e\xdb\x18Ly\xaf(%\xad\u07cbMc\x13\xed\xed\xed\x9cz\xea\xaa\x1d\x1f\xfb\xd8G\xaf\x16B\xf4\x02,_\xbe\xfc7\xbeY\xbfw1\xff\xecg?\x9b\xfc\xf9/\xff\xe6\x93\x1fY\xff\xc8\x03\x13\x83r\xc9v\xb31;n\fR:\x84\xa1Gc\xeb\x14\x96\x9e\xf3~\xeaZ&S*\x0e\xa7\n9't\xe7I\x896\x95\x9d\x9bTa\xac\x02\x95S9\xd8\xd1N\x82T.B(\x8c\b\xd0\xc2&\x88W\xa6g\xac\xec\u0764\xc0\xe9\xf4\x10ME\xdc\x1f-\xfc\x10(j\u06dd\xbb\"\x19T\"\xe9\xba\x05F\x1a\xfc\x10\x86}M94Q\x87\x1d\x15\xe8\x88\x0f\xb0\xb0\x88\x88\xd2X\xe2\xc1'\x11'\u070d1\nK\xfdt&u\xbeHOC\xa7\xb8\x01i\xa2\xa2\x1du\xe2\x91O\x01B\xc5\x03I\xa6\xca@LK\x119\xe0\xc9dH\x84\xe8\x18\xac\x04H\xd7~M\x95z\xed\xb5\x16\x11\xa9e\x12#}\x1d\xdd\xc1\xb1\t\u007fh \xf0|\xc4h\x81\x05g^\xc6\xd1\x03[X\xfb\xbd/Q\x1c\xe8\xe5\x81o\xfc#\xed\x1dSi\xba\xf0tj\x95\xa6\xd5\r#\xa2\xec\xe4\xf5\x1fI\x10+\x13\x9e!A\x10\x92\xaf\xa9\xa1\xaf\xb7\x8f\xaf\u007f\xfd[\xac[\xf78\xe3'\xcd\u4d0b\xaf\xa6\xbeu*\u00e3#v87\x9eGO\xcf\xc0GJ\xa6\xf4)V0\xb6q:\x91\xa3\xd2\x00\x8e [\xef\xe08\xb2bn\xf5\x9b\x98Y*>/d$\xb21\x834\x01\fht\xd1@`UR\xd2\x11v\xbe\u00c8\xe4\xebUZ\x9e1\u007f3\x86\x92\x96L\x9e\xb1\x9c\x86\x97\x1e\xa2\xa7\xe7\b\x86\xac\u0154\xad/\x93)\x97\u02e2\xab\xab{U\x10x3\x1d'\xb3\xe7\xc2\v/N\x9e\xda\u04a5\xcby\xe8\xa1_s\xe1\x85k0\xc6\xf0\xd5\x1boPB\x88\"\xf0#c\xcc=\xd7_\u007f\xed%\x1d\x1d\x93\xfff\xf3\xe6\xad+\xf7\xec\xd9C\xa1X\xc0D\xba}m*\n\xb5\xb8\xe1,\x17\v\xec~\xe61v?\xf3\x18M\x13:\x980k\x01\x93\xe7-f\xd2\u0725\xb4M\x9bMMC3N&\x83#\x05\xbeW\xa68<\xc4\xc0\u04579\xb8\xe9Y\x0eo}\x91#;7\xd1\u007f\xe4P\xd5=,\x95\xaa*\xe4mm\xe3X\xb6l\xf9\x81\xcb.\xbb\xec\x1d\x9d\x9d\u04f7\x00\xdcr\xcb\xcd,]\xba\x82?x1\xff\xc2\x17>\xcf\xe7>\xf7y\x00\xee\xf8\u065do\xbb\xe6\v\xff\xe7O\xbb\x0e\x1f\x8aX\xe1\xc8GA\x9b\x84\x95\x96B1\uf5372m\xc1j|\xafT\x95\x9c=\xd6M\xac\xaaS\x16\x86\x8a\x9dd\x95\xa9C\xd5\xfa\xab\xea\u06a3\x91\xf5X/-\x8cFF\xc1\xafFT\x8c\xf3c\uf4b8\xb3\x16\xc6Nr\u0274\xd66.\ua041\x92\xb6\xea\x96\xd8\x18\v\x83\xb1\xc1\xe4\xe8\xc0P.\x85\x84e\x8d\x1b\xe5%&\x98v\u0509\x93\x1e\x9f\xaf8\x83Uu\xb7\xc9\"\x8e\xd4-\x15[\x83\x94z\xc4\xc8H~)+\xd3{\bl\u53a4\x9a\xe67l|\xa2\x8a\u07cad\x8b\x95\xe1%G\br\x11w \x85\xdd\x0f$\xc90\xaa\ud0a4$\bt4\x19K\x1c\x17i_\xcf\u8865\xc0+{\xe4\xca\x19\xcex\u04df\u04f3{\a[\x1f\xbf\x8b\ue75b\xf8\xf9\xbf\xfc\x1d\xed\x13\xbeG\xc3\xf2Nra\x89\x1a\xa5\xf1\xf5\u0262\xfd\xdb$\x88a\x18\xe2y>\x9e\x17\xe0\a\x01\xd9\\\x8e\xb2\xe7\xf3\x9d\xef\xdc\u011dw\xdeA\xbe\xb6\x81\x95\x17}\x80\u0273O\xa5P\x18%\f\xc2\xc8\x02\xda$\xa6r\t\xe7\x92\xccW\xd8\xe1\x16Q\xa5\x81H\xb9\xf3\x88\xd4L\x82\xb1\xfc\x89S\xa3\xc8\xd5\u02a8\xdb\x14c\uee0a>\xbcJuf\xacp@\xd6: 2\x18\x05\f[\x82\xc5\bc\xe7\xf7\xc4\x18W\xdc(\xfdb,\xd4\xe7\x05!\xed\x9dKhi\x9bB\xcf\xf1\xc3h\xadQ\x11\xdf#\xa5\x12\x85b\x91}\xfb\xf6\xca{\xef\xbdw%\xb0g\xecky\xe1\x85k\xd2Ma\xf8\xf0\xc3\x0f\xc9{\xee\xb9\xc7\x11B\x8c\x02?6\xc6<q\xe3\x8d7\xfc\xe5\x83\x0f>\xf4\xc1-[\xb6\x8c\xeb\xee\xeeft\xb4\x80R\x8eA\"\xaa%\u0155\xaeo\xa0\xfb0\x03\u0747\u067e\xee\xd7\xe4\xea\x1a\xa8mj!SSg\x9d\x1e#'X\xafT\xa484\xc0p\ufc6a\xf76&_\x85\xact\xe4Z\x87b\xe2\u0109\xac\\\xb9\xf2\xc0y\xe7\x9d{\xf9\xdb\xdf~\xc5\x16\x80\x1bn\xf8\xbf\xe2\x03\x1f\xf8\xd0o\xed{~\xafb\xfe\xd9\xcf\xfe=\x9f\xfb\xdc\xe71\u01a8\xcb\xdf~\xf9\xe7\x0e\x1e\xd8_\x8b1HiI\x99x\xba3f\x8a;f\x9f\xce\xf2\xf3\xaf\xc6\xcd\xd6Q*\xfe?\xf6\xde;\u0732\xab\xba\xf2\xfd\u0375\xf6>\xe7\xe6{+\xe7*\x85RD9`\x81$\x04B \x92\rn0m\f4B\xed6\xc16m\xbb\xbb\xdd\xee\xee\x87\xc1&\xf8\xb5\x03`c\x03\xc6 \x82\xdbX\xc6&H\xc6`L\v\t0 \x19$\x94\x91J\x15\xa5\n\xaa\\7\x9e\xb0\xf7^\xf3\xfd\xb1\xd6N\u772a\x92\x84x_\x9b\xea\xfa\xbe\v\xa5[7\x9c\xb0\xf7Xs\x8e9\xe6\x18\xd3 \xa6>\xe7D\xfb\\\xc4J@\xef-\xc6\xf5\xd8\xe3\xfe\x9a\xc3M\u0e6d\x05\xa2B\xff\xed\x01\xbd\xbe\xc8\xe3\x03\xa2\rX?\xc0\xc1\x80D\xc6\xf7\x85N\x83U\xae\xcfN\x94\xc4y\x8b\u05eeC;J\xdaq\xd0u\f\x17\xaa\x01\xed\xd1[Ulu\xb4\"&\xaa\x96FR\x1a^\xf9\r\u03bc\u0291\xca\xd6^\x0e\xe2\xb64+\xab\f\xa9\x8a\x90\t\xa9\x94\xf4\x15\xb5\xa5\x0f\xec\xf0\xab\xda\xd2\xf0[\xa3\x00\x91\x85!\xeb\u0346\n]O\xb1\xecPv\bF\x15q\xc6o\x1c\x86\xe9\xbd\x03\x92\xc4!\x89\ay\u007f0\x1a\xba\xd3\xf3LN\xad\xe6\x9a\u05fe\x83\xf9\xc3\xfb\xd9q\xff\xb7\xd9|\xc7\xd7\xf9\xebw\xfd'\xec;\xfe\x18s\xf6j\xcen\xce\u04f4\x92\x1b\xf4\xfd\xdf?5j%\xa3\xd3\xe9\x86\xc5 o\xb3\x9a$)7\xdc\xf0)>\xf2\x91\x8f\xd2M2\x9e\xfd\xfc\u007f\xc7Y?\xf5r2'dIRvn.\x18;Uc\f%l@\xda(t\xcez\x94\xe1Hy\xdbe\x06h\n\xcd\u0248\xa8a\x8aJ\xba\x8fZ\xae\xeaj+\xffh\x8c\xc1X\x83\x0ey\xf7M\xc4\xc1\xacCS\x83D\x95\x03!-\xfd\x8fLAe\xe6A\x18~\xc8\x19\x8d-c\xdd)\xe7\xf1\xe8\xb6{H\x92\xa4\xb0\xa0\x15\xe3\x03\x8fgg\xe7\xe2\xef~\xf7\xf63\x9e\xc8k{\xf5\xd5\xcfw@\xf7\xc3\x1f\xfe3y\xcb[~YE\xe41\xe0\xbft:\xad\x0f\xbe\xf3\x9d\xbf\xf3k\xb7\xdf~\xfb/n\u0672u|\u03de\u0752e\xae\xdbh4bU\x95,\xcbJm|\xa5\x93\ah\xcf\xcd\u041e\x9b9\xee\xef6\xf9\xba\u007f\xf8\u007fk\f\xaa\x9a\x1ac\xa2\xd3O?\x8d\xcb/\xbf\xfc\xa1\u05fd\xeeu\xaf\xba\xe2\x8a+\x1f\x00\xf8\xe5_~\xab\xbc\xedm\xbf~\xcc\x06\xf6)\x81\xf9g?{#\xaf~\xf5\xcf=\x05\xff\xf0\x00\x00 \x00IDAT\x17\x15\xeb\u007f\xff\xef\xbf\xf5\xae\xfb\xef\u007f\xe0\xe2#\x87\x0e\xf9\x9a\xd1\xd6WC\xb24alj\x05\xe7_\xf9:\x96\xad=\x9bvk\u059f\xe9Zsw\xaa\xd4\xdeR\xd7\xe4I\x8f\x1a\xb5\xee\xe4\u0783\xe0R\xf3\xe9\xeeUhK\x18\x94\x92\x0fO(\xb7u\xb4R\xfbK\x84\x97QY\x81X\x8a\xe9\x9ev\x80T\x90\x18\x9c\n\x92x\xdf\u7d2b\xb8\x14\xe2\xa0\x1c)x\xff\x8a\xefJ-\xbe\xae\"\x0e\xad\xaa\x05j\xa6G\xf9\u07f5^\x9dKE\x8d\xa3\xbd\x0ex\xda\xeb\x8aW\xbfU\x9d\b\x1a\xf9OD\x91!\x1a3\xd8E\rl\xecA\xdcJ]\x9aFO\ua560X\x04\u027c$\xcbW\xee\xe1\x86K\x95d.\xf5V\xab\x99w\xa1\u031c2?=\xc3\xca\xf5\xe7\xf1\x82\u05fe\x93\x9b?\xf4\xab\xec{\xec!\xee\xfe\xa7\xbf\xc3D\u00d8\xff\xf1\xfb\x8c\x9c\xb3\x92\x8d2\xef=\xe7\xff/~\x17\u007f\x92$\xa5\xd3\xe9\xd2MR\xd2,%n4\xc82\xc7g>\xf37\xfc\xe9\x9f~\x88\xb9\xf9Y.\xbc\xfcg\xb9\xf0\xaa\xd7a\xa3a\x16\xe6\xe7Kh\xd6z\x17V\\?\xb9\xa2\xc9D=\xb3\xa6\xfe2JC\xd7\xe6\"C<\x1a\xd1\x18\u036d\x9a\xf5\xb8\xb2\x1b\xa1\xec\xa6ErE[\xe2\x1b\xc7q\xef\x85d\xd5\xf9\xeb\xcd:\\\xaad\x9a\x15\u02b2\xc2>BJ\x95[FB\xd7\u016c>\xe5bFn\xbf\x89C\a\xf7\xe2\xe2\x18\x1b\xf9\xc8\xc94M1\xc6\xd0\xedv_\x04\xfc.\xc0\xe6\u035b\u0638\xf1\xf4c>\u0737\xbc\xe5\x97U+\xf1\x8e\xcd\xe6\xf0\xa3\xc0o|\xf5\xab_\xf9\xc7/|\xe1\v\xbf\xf3\xe0\x83?\xbcl\u01ce\x1d\x8d\x9d;w\xa1\xaai\x1c\xc7VUE\x8b<\xcez,\x9e\xcb\xdb\xd8*u\x94\x0fw\x03x\x17\x15\xb9\xf7t\xd14M\xdd\xc4\xc4Dt\xca)\xa7p\xed\xb5/\xfc\xa7w\xbe\xf3\x1d\xff%\x8a\x9a\x0f\x00\xfc\xfa\xaf\xff\x9a\xbc\xff\xfd\x1f8\xee\xad\xf1\x94t\xe6\xaf~\xf5\xcf\xf3\xc1\x0f\xfe\xb1\x05\xb8\xed\xb6\xafo\xbc\xed\xb6o\xfe\u008e\x1d;\x00\x12k}]W<Q\xa7\x881\x9cv\xc1\x8b9\xfd\xe2\x9f!\t\xc1\xc9%4\x94\x95\xb6\xe6\xe9\xd4\xf4xn\x86\x96O\xab\x8b\f\bN\x85L\ri\xfe\xe1\f\xa9Z\x12\x8dH4\"U\x1b>\xf2\u007f\xb7t\x9d!q\x11\xa9\xb3\xa4\x1a\x91jLFL&M2\x19\"3C\xa4v\x88\xcc\f\xe3\xec0\x1a\r\x03C\x18mbhb\xb5I\x94\u0158,\xc6h\x03#MT\x9b8i\x10\xd9!\xa2x\b\x1b7\x89\xa2!\xa2\xa8\xe9?g\x87\x88l\x83\xd84\x88\x8cW\x93\u0628A\x145\x88L\x93H\xe2\xfa\a\x11\x111\x111\x96\b+\x91W\xa4`\v};=\x1dM\xf5P\xa2\x92$$\xc5\x10\x95B\xd9`RG\x94)C\xaa4Rh\xa04\x9b\xc6W]\x91\xff\x90\xd8x)\xe6\x90A\x86\xa4\xf8\xbb\x19\xb2\xd8a\u007fs\u01e3\x16\x19\xb20d`\xd8\x12OD\x98\x89\x18\x9a\x96\xb0}L\x16\xfc\xa9\x17ff9\xe5\u072b\xb9\xfa\u07fe\x9d\xc9%k\x00\xb8\xeb\xcb\u007f\xc9_\xbd\xe7\xbf\xf1\xad\a\xf7\xb1\u06cd\xfa\x83\xe2\x04/\xcd\xf3C\xb3\x9b\xa4\xb4\xda\x1d_\x91\xa7)\x8dF\x13k-7\xde\xf8\xb7\xfc\xd1\xfb>\xc0\xfe\x03\xfb8\xfb\xa2kx\xd6K\xde\xc2\xd0\xf8R\xda\v\xf3\xfe^s=\x90\xac\xd5\x05\x1c\x0f\xaa\xc6D=\x86W\xd2W\xf8\x14\xdeXV0MCs\xccb\x1b&tj\xdaS0\xd5|i\x8bR\xa3J\x8b\x16\xdar\r\x8a\x8e\u0448xQL<j\x89\"!\x8e\r6\xaa\xbb5*\xf5\xbd\v\x17\xac\x94'W\x9d\xc1\xa2\xa5k|\xe4\x9a+}\xccm\x14177\u01d6\xcd[\xa6Tu\x1c8.\x90W)\x0f\x80O|\xe2\xe3\xc5\u7bbd\xf6\xc5\xff\xf4\xe1\x0f\xff\xf9\xf3\xdf\xf0\x867\xfc\xf2e\x97]\xf6\xf7\xd7\\\xf3|\xce:\xeb\xccH\u0108s\xae\x16\xe9X\xfd\x88\xac\x8f\xce3Q\xe4\x95)q\x8c\x8d\xa2\xb0\xcdi\u02ea\xbc\xa4\x99e\xed\u06b5\xf6\x9ak\xaeI\u007f\xf1\x17\xaf\u007f\xf7\xbb\xdf\xfd\xdek\xa3\xa8y/\xc0_\xfe\u59df\x10\x90?\xe5\xca\xfc\x96[\xbef\x9e\xff\xfc\x17d\xaa*\xd7]\xf7\x86\xff\xbcy\xf3\x96\rI\xb7\x8b\xb16\x92@\xaf\x14\xa7\x94KY\xb9\xe1<.\xb9\xfa?0<:\u51deG\xa5Gr\xefq)&\x91%\xaf[\xd1X\x8bbqD\xe2\x88\xc5\xd10)V2b\x93\x11\x89\u00c8\xd6\x02\x9bJ&0\x18\u072b)\x04-N\reR\xa1\x14\x87\x841\x06\x1b\x1b2\x1b\x87\x03#\"S\x13\xbe\x1e\xc8\xfc\xaa|\x92:\xba\xce\xeb\xcbM\xa8\"L\xe5\xa7y\x99\xa3\x06\u04efj\xc5\x1c\x1e\x95\xabf\u06d6\xb7\xc0\xc0-!\xa9\u01fdIM%\xd3\x1fP5pmT<\xb8G\x0eL\n\xa6\xed\x90\xf9\f\x86-\xb5]\xfbJ[\x91\xbfv\u0554qS\x1dQW\xd8$\xdb\x14\u04a6\xf19\x92F\xc8DqN\xe8v:\xa0p\xcee\xaf\xa4\xbb0\xc7W>\xf5_i\xcd\x1f\xe1\xce/\u007f\x12#\x86\xf8\x1d\xef\xe5\xa5\x17\xac`\x95i\xa3\xa9\u00dd\x90@\ue2e0\x1c\u023b\xdd\x04\xe7\x1c\u0366\xcf\xc0\xfc\xf4\xa7\xff\x8a?\xf8\x83\xf7\xb1o\xdf\xe3\x9c~\u0795\\\xfe\xd3ocb\xd9\x06\xda\xf3\xb3!\u078f~\x806\xa1\x1f\r\x1b\xd3&X`\xe4r\x01A\x06z\x1a\xa9\x88\x1f\x90GBc\xd8\xf8\x83\xdb\x1cM\xf5R\xad\xc2{\xaf\xc6RY\x95\xdblHX\xae\x91\xa1\xc8\xdf\xdb\xf8`\x18\x13y\x03=\xa7\xa5*R+j\x10\x102\x97\x11\x0fO\xb2\xfa\xe4\xf3xt\xeb\xbddY\xf09\x0f\x1d\xc0\xec\xdc\x1c\x87\x8e\x1c^\xf6\xe0\x83\xf7=\v\xf8\xa7'\xfb\x1e\xbc\xf1\x8d\xff\x9eO|\xe2\xe3l\u07fe\x83\xdf\xf9\x9d\xdfED\x16\x80\x0f\x01\x1f\xfa\xc2\x17\xfe\xee\xd5_\xfb\xda-W\x9ey\xe6\x99/\u07bf\u007f\xff\xa9\x0f?\xbc\x89\xd9\xd9Y\xb2,+\ue0fc\xc2/\xabq)l\xa7\x15\xaf\n\xf3n\x8f\x868\x8eY\xb6l\x19g\x9cq\x06\xeb\u05ef\xbb\xf95\xaf\xf9\xf9\x0f\\u\xd5\xf3n\xcd\x1f\xcb?\xfe\xe3\x97y\u044b^\xf2\x84\x9b\xd5'\r\xe6w\xdf}\x17\xef~\xf7\xbb-\xe0n\xb8\xe1c/\u07b1c\u01db\xf6\xef\u07c7\x18\xa3\"\xf5\xba\xcae\x19C\xa3S\\|\xf5/\xb2\xe2\xa4\vH:\v~S\xaag\u02cb\xca\"\x8d\xf6L^\xacu4\xa3\x94\x86\xcd\x18\x96\x84\x86\xa4\fI\x97a\xe92b\x13\x9a\x92\xd00)q\x0e\xe6&+9\xac\x82\xb7\xf6\v*V|[\xe7\x804\x04\x19f\xea\u056fF\\q\x11:\x04U\x03\x91\xc5\x19K7\xb3$YL\xd7\u0174\\L[c:\x123\xa7\x11F\x84Fp\x8bS'\xb8\xcc\xe0\x9c\xff\xb9\x19\xa6\x18h\xf6\xc4\x1e\xd6\x14]\xa6\xea'\xaeT\x92\xed\xeb\x19$\xd5\xd7E\n(\u05c1\xf3\x02\xa9\xef\x9a\xd1\x176\x98\x17X\xa9C\xe6\x1dL(\f\x1b\xfa\xcd\xcd{\x84\xa1R\x0e\xba\x8cJ_a\x16\x19C\xbbaH[Bd\xbd\x8aG\x9d7-\xeb\xb6\xdaH\n\x17^\xf9z\xd2\xd6<_\xfb\xec\xef\u041a\x9f\xe6{\xffp\x03Y\xda!z\xe7\xef\xf1\xaa\xcb\xd61f\xbb$\xdd\xf4\x84\x92,V\x81\xbc\x9d\x03\xb9*###\xb4Zm>\xfd\xe9\xbf\xe4\x83\x1f\xfc\x10\xfb\xf6\xede\xe39\xcf\xe2\xaaW\xfc'\x96\xac:\x9d\xf6\xfc\\!\xed\u04f0}\x9c\x83_!\xd5\x15?\x18\x97P1J\xbf\\\xa0\xf6~kP99+D\rCc\xd4z7\xce\xda\xf5U\u05dd)\x83\xae\xd9z%\xa1H\xcfj\x82b\x9a\x06\x9d\x8cQ#\x18u\x98Lp\xa9\xef\xe8\xb3\xf0\x18sS-\x05\xd24\xa11<\u02aa\xd3/f\xe8\u06dfgnn\x16\xa7.\xb8\n\nY\x9a17;?~\xdbm\xdfX\xffT\u07cb7\xbe\xf1\xdf\xd7\xfe\xfb\x93\x9f\xbcA\xae\xbb\xeez\xfd\u065f}\xd5g\x81\xcf\xee\u0739\xfd\xec\x1bn\xf8\xe4E\xe7\x9dw\xfe/\xec\u07bd\xeb\xca\xc7\x1e\xdb9\xb6g\xcfnZ\xad6Y\x96\x9bx\xd5\r\x05}\xd5ni4b\xc6\xc6F9\xe5\x94SY\xbcx\xc9c\x13\x13\xe37?\xebY\xcf\xfa\xf6u\u05fd\xf1\x8bA]\xc3_\xff\xf5g\u456f\xfcY\xfd\xe2\x17ozR\x8f\xfbI\x83\xf9[\xdf\xfa+\xf2\x9d\xef|'QU\xfb3?\xf3\xd3o\xbc\xf7\xde\xfb\x82\xca\u0358\xdcs\xa5\xfa\x86\x9eu\xf1\xcfp\xe6\xc5?\x83s)i\x9a\x14\xff\x96\x05g5*\xa0dEi\u0614!\xd3e,\xea0\x16\xb7\x19\x8a\x12\x86\xe2\x84f\x94\x10\x9b\x8c\xa6&44#\x96\xd4W\xe1\x15@s\xf9\v\x17\x92L\xf2\u007f1E\r\xa2\x95\\g\xc1\xa9\t\x16\x95a\x80n\xb4\xc2\x15\xe6\xaelJf\xfd\xae|\x86\r\U0010d84b\xa1E\x8c\x03\xac\x962=\xd4\xcb\xf7<\xa5\x13\xd1\xca\x1a\xb4\xb3\x98D-i\xc8\xd2L\xd4\xd0\xcd,\x1d\x17\xd3uQi\xf4U9\x80\xf2}Q#\xa5\x11W\u0756@\x8f\xcaWr\f-~\x11\xc2\xe1r]\xa1@'C\u06e1:\xef\xfbV\xad5N\"\xa5}\xa3\x88\x86%\t-\x00$\xb2~\x13\xb0\x13\xf2V%\x8fn\xf1V\xf6t:m\xd4)\x17=\uf348\x8d\xb9\xe5o\xdf\xc5\xdc\xf4>\xee\xfa\xea_\xf1\xfbs\u04d8\xdf{\x1f\xff\xf6\xca\xd3h6\"\u04a4M\xe6N\x1c \xeftSZ\xed6\xddn\x02x\xbf\x95\xfd\xfb\xf7\xf3\x17\u007f\xf1q>\xf1\x89O23}\x98\xd3\u03bd\x9c\xe7\xbc\xe27X\xbe\xee\x19\xb4\u06c1ZQ)\u02e3\xea\x06X^\x1d\xe6\x89Q6\xea3\x88\xeb\xab\x02BI\xec\x8c@$D\u00c6h\xccz\x95\xabj\x0f\x0f_W\x95Uw\x1c{y\xf8\xde\rfo#\x10$\x89\rA',\x96\x98\x86\x80\xeb*\x9a*Y\xe6z\xe8y\xc1\x89\x92\u0186\x89u\x1bY\xbcj\x1d\xb3\x0f\xdd\xe7\x15&\x01\xcc\xd34\xc5X\u04dc\x99\x99\xbd\x1c\xf8\u060f\xfa\xde|\xf4\xa3\x1f\xe1\xba\xeb\xae\xd7/}\xe9f\u067au\xab}\xdb\xdb~-[\xbb\xf6\xa4\a\x81\aU\xf5+7\xdc\xf0\xf1\xa5\xd3\xd3\xd3\xcf\u0777o\u07f3\xf6\xee\xdd{F\x9a\xa6\x97\xee\u0673\xc7\xce\xcd\xcdfI\xe2+\xf6f\xb3!\xabV\xad2\u02d6-\xdf\x1fE\xf6\xd6\x15+V\xdc\xdbj\xb5o\xbb\xf6\xda\x17\x1e\xb8\xea\xaa\xe7=\xfc\u044f\xfe\x05o|\xe3\xf5\xbc\xeb]\xbf\x1b\xbd\xf5\xado\u0396,Y\xfe\x94\u0298'\x05\xe6\u007f\xf5W\u007f\xc9k_\xfbz\x05x\xff\xfb\xdf\xf7+[\xb7n}\u0561C\x87\x101&\x0f0\xc8\u06dd,\xed\xb0b\xfdy\\\xf0\xdc724\xba\x88n\xa7\x85SC\xa6~\xa8bP\x1a&e\xd8&LF\v\x8cGm\u01a2\x0e\xc36a\xc8vi\xda\x04k\x1d\x91u\xc46\r^\xc9B\x949\xbf(\xa3y\xe8jYaD\x92o\\j\xe1\x87.\x95\xaaV+2-\xff\xb5\xfe\x00)\x82\xe6*b\x13'%\xf4[\xc90Vi\x900\f\xa5\x99\xb7U\x0f\xb6A\xe1\x81+I\xbe\xdc0,\u0448\xc4Y25t\u0552:K\xa2\x96\xc4YZY\x83\xb9\xb4\xc9|6D;\x8b\xe9d\x11\xed,\xa6\xed<\xf8g\xcex\x85Gx\xbd$\x1cL\xf5\x84\xf3\xa3\u0718\xb9t\xac\"\ub522\x82\xf2\xca\x112\xf5+\u0459\xc2B\x06\x13.\x80o\x0f\x87Z\x1d\xa6\xa9\x1fJ\x99\x1a\u0397z4\x01\x86\x87,:bI\xdb\xce\xfbT4(\xaas\x14\xba\x9d\x16\x99\x89\xb9\xe09\xff\x8e\xb81\u00ad\x9f{7\a\xf7n\xe3\x87\xdf\xfe\x12\xefz\xdb\f\x87\xfe\xeb\xefr\u077f\xb9\x8a\xa9\xc6\b\x92.\x90\xb9\xe3\xcf\xdc\xfe5\x03\xb9s\x8eN\xa7\uba55$\xa3\u0448i4\x1b\xfc\xf0\x87\x0f\xf1\xa1?\xfb\b_\xfc\xe2\x17I\x92.\xa7_p5W\xbe\xfc\xd7X\xbe\xf6,Z\xf3sE`H>0\xec\xe5\xbf\xf3hF\x9f\x16\x15\xd5b\x1a\xebB\x83\xf2\xfbT\x84\u0302\xb3\x105\x84x\xccb\x1af \xaf\x9e\u007f\x97\xab\xca\x19\xe9\x8dB+7\xcb\nc6\xa9\u020d\xf3\an\x053f\xbd\xc4u.C\x13\x87I\xfd\x02\x8es\x81\x8e\x14\x1f0\xddM\x13\u2265\xac9\xf3Bv<|\x1fY\x96\x95\x1d\x87\x88v:]\u067am\xdb\xe8\xd3\xf1\xfe\xfc\xd2/\xbd\xb9x\xb9\xde\xf6\xb6_K\xb7l\xd9$\xdf\xfc\xe6\xb7\xe2\x1bo\xfc\xac\x84%\x9e\x83\xc0\u00ea\xfa\xc9\xe9\xe9\x83c\xb7\xdcr\xeb\xe4\x9dw\xde%{\xf7\xee\xd5V\xab\x8ds\x8e\xf1\xf1Q\xb9\xe0\x82\v\xe5\xf2\u02df\xdd9\xff\xfc\v\x0f\x06\xea\x86\xf7\xbe\xf7\xf7\x00x\xd9\xcb^\x1a\xfd\xc6o\xfc\xba\xbb\xfa\xeak\u04b7\xbf\xfd\xb7\x9f\xfa\xb5\xf4T\xbe\xe9;\xdf\xf9\xf6\xea\xf7\xbc\xe7=\xdf\xfd\xfa\xd7o]\xdfj\xb5\x82\xbfGXo\n\v(\xcd\xe11\x9e\xf7s\xef\xe0\x82\xe7^O\xbb\xe5\xe9\x15c\x1cC\xa6\xcbx\xd4f2n1\x15\xcf3\x1e\xb7\x18\xb3\x1d\x9a&\xc5\x18_\x89\x1aQR1\x18\xeb\u0273\xcc\x18\xc4(F\x15\x9b\xf9\xe0X_X\x8a\aS\xa9\f^\xd4ar}uu\xa8\xaau\xa6\u064a\xab\x0f\x12\xf3!k\xc9\x0ec$\x0f}\xf6\x15\xba\xcd\xd70\x8b\xc8(\xadK\xb2\xd4oFV\x01\xbd\x9e}(\xc1\xb6\xd6\xf3\xf2\x8a\x1f\xe0v3K\xe2\":.\xa2\xed\x1a,\xa413\xe9\b\x87\x92Q\x8e$\u00f4\xb2\x06Y\x18\xe4\xaa\xfaX-\x11\x87\xad\xcc\x06t\xc0[YU\xee\xf7\xd6\xf4\xf9\xf3\x1a\x8a\rQ$\u0218\u016ci\u0090\xad\u0336\xf4(\x17J\xdd'\xa7\\\xf5\x0f\xfa \x11\xba\xad\x8c\u03be.\xe9B\xe6_\xd1\x04\xa4\xebB\x86\xa8?\x15\xa2\xb8\xc9Ps\x84\xed\x0f\xde\u02ad\x9f\u007f\x0f\xdb~\xe8#\xb2&W\x9c\xca/\\\xff\x1b\xbc\xe9\xba\xd7q\xe6\x86\t\u0439\x90\x1a#?A \xee/\x93$H\x0f\xfd\x8a\xbe\xf7$\a\xf8\xdf\xff\xfb\x16\xfe\xf4O?\xcc\x1dw\xdcA\xa39\u0139\x97\xbd\x8c\xcb^\xf4&\x16\xaf\xdaH{a\x864I\xcb\xf9\x8cJ\xb0G\xf6T_\xfe\x1e\xf8\u0160\xc8\xe7\xb7\u06a8\xef=T\xd5>\xabB\x17\x19\xd2\xd8+\xb8\x86'\"\x86\x975\xb09\xc52\xb0\x1b\xec\x8d%\xee'\xf7\xc2\xee>I\xbbE\x9a$a#\xdbk\xb6\x9d\xba\xc2\xd8G\x83\x8d\xb3.d\xb8\xd9\f\xd7\xf5\xd6\xd1Y\x9a\x87\x94\x85\x1fl-c\xcb\xc7\xd9\xf3\xd0W\xb9\xe9\x0f\xff3\xdd4\xa3\xd1h\xfa\x19M\x96\xb1x\xf1\x14\xe7\x9fw\xce?~\xe9\xef\xbf\xf4b\x80Gwlc\xfd\x86\x93\u007f,\xef\xe3\xddw\xdf)\x17\\p\xf1\x93.5>\xf8\xc1?\x91\v/<_\xaf\xb8\u2aa7\xed\xb1<\xe1\xca\xfc\x86\x1b>&\xd7_\xff\x8b\n\xf0\xb9\xcf}\xee?\xdfs\u03fd\xeb[\xadV\xb12\xae\x85\x1e\xd9\xe0\\\xc2I\u7f00\x93/x9I\xeah\xd2fj\xa8\xcd\xe2\xc6,K\x9b3LF-\x86l\x12\x86\x95.\x00k\xa9\xadQ#\x88U\xc4\xf8\xab\xd5\xe2\xbc;Cn\x8e\xdf\x13\u03e3\x02\x11\xce{P8oA\xa99\x8dS\xc4\xc7\xf5\xd8\xceV\xeb\x87\xf0W\x93[\u0245\x03B\xc2\u007f\x8bh\x1f4\xd6d\x81\x15\xfd\x9eV\xdd\xd2\\8\u07e8\xef\t\xe5\x81\u0222\x10\x89\xa3iRT\u06e1s\xf0|{\xa2\x1e\xd8\xe7\x92&3\xe90s\xe9\x10sY\x93\xb9\xb4I;k\xd0v1\x9d,&US\x1c\x80\xa6\x10\x97k\x11\x1a\xa1\x03\xd9K\x8a!T>\x84%QoY0\x94'\xa9K\xb1*\xcd1H\x1bz+\xf4\x9c2k\bQ\xd3\xe0:\x19i\nQ\xee@ \xe5\xe9\x99$\x1dP\xe5\xa4s\x9e\xcf\u02e6\x96s\xdb\xe7\xdf\xcb}\xdf\xfd\x1c\xd3{\xb7\xf0\x91?\xf8M\xb6\xfc\xf0>\xde\xfc\x1f~\x85\u02dfy6\xe3\xa3]\xa2(\xf5\xaf\u047f\xf2*]\x80,s\x85\x85m\xa7\xdbE\xc4099\u026e\u077b\xf9\xdc\xe7>\xcf'>\xf1)vl\xdb\xc6\u0112\x95\\z\xf5/p\xe1U\xbf@sd1\v\xb3Gp\x9a\x15\xf5o\xd9\fJmP\x98\x87q\x8b\x8d\x91\x82'\x1f !\xac\x8cR\xd4\b\xce\xfa\xfd\x83F\xc3\xd0\x18\xb3\u0626\xfch@N)f\x10c\xbc\xfcT\f\x98P\u03c7\xa7\xa2&l\x83\n\u0208/\b\x99\xf1\xdeDe&y\u0634\xce2\\\n\xe3+Nb\u046a\xb5\xec\u067e\x05\xd5\x06\x1a\xf6Ifff\x19\x19\x19=OU\x9f+\"\xb7\xed\u06ff\xff\xc7\x16r\xf5T\x80\x1c\xe0W\u007f\xf5mO\xfb\xe3yB`\xfe\x8ew\xbc\x9d\x1c\u023f\xf1\x8d[/|\xf7\xbb\xdf\xf3\xeb\xfb\x0f\x1c\xf0\xb8k\xad\x94\v\xbfB\x9a&,Zy:\x17_\xf5Z6,\x1fa4\xd9\u02b2\xb1y\x964f\x19\xb1\x1db\x93\x16tAUl/a\xffW\x83\x0e\xdaX-\x92uL\xa5as\x01\xd0\x05%B1\xeaC[\xadj\xa1V\x91Z\xb1\xec\xe5q`\u020aX\xaa\xd2\tP\x04\xacq~\x81\x06\x10\xe3j\x1b\xa5\xf5\x14\x94\x92{\xd7\xdeK\xba\a\xd9D\xf0{\xf0\xa6\xde\xd2JE>\xe6zt\x04&\x1cj\x16\xa1I\xc68m\x966\xfc\xefs\b\xa9\x8bh\xb9\x98\xf9\xb4\xc9L2\u0311d\x84\x83\xddQ\xa6\x93\x11f\xd2a\xba\xce\x06.\u0495@-=\x83\xd7\xea^Gx-\x9c\x06\xd7\xc5N\x99cU\x8clU\x06\xc8\u0434\xf4\xb3\xd1j\xd6G\xa9\u007f\x17\x03v\xc8`\xe6\x8dW\xa78\x17|B\xea`\x90\xa6]\x16\xe6\xa6Y\xb2\xfa\x19\xbc\xe8\xf5\u007f\xc8\u0112\xb5|\xff\x96\x8f\xd3i\xcd\xf1O_\xfc\b\x9b\x1f\xfa\x01\xaf\u007f\xfd\xaf\xf0\xaaW\xbc\x82\x8d\xa7\x8e\x11\xd9\x0e\xe0\x82g\u01bf\xcej\xbc\x9b\xa6t:\t\u076e7\xc3\x1a\x19\x1e%u)\xb7\xde\xf6\r>\xf5\xa9Os\xeb\u05ff\xce\xdc\xfc<\xabO>\x9b\x9f\xba\xf6\xdfs\xe6%/\x05c\x99_\x98\r\x87\xb4)\xfc{\xaa\U000196e0i\xd0u[\x1bC\u0413\xf7\x8f\xb4\xc3\x15]\xd9\x15r\xd6\a\x9a\x98Hh\x8cx\xae\\\xe4h\xbar\xa9\u02e6D\x06\xb6\xfcZQn\xe5v\xc99\xe5Y\xec^h\xe9\xcb+\xaa\x9e\xff\x1e\x01\xe3\xac\x1f\xce\xe7a(\xb9U\x84\x13\xba\xf3\x1d\xa2\xc6\x14K\u059d\u02ae-\x9bp\xea0j\x8a\x8e\xe3\xf1=\x8f\xa7\xb7|\xfd\x96\x04\u0ef7\xdfqB\f\u045f\x10\x98\xaf\\\xb9\xb2\xf8\xfb\x8d7\xfe\xcd\xff\xb8\xe7\xde\xfb\xe8\xb4\xdb\x18\x13\x891\xd6k=\xf1-N\xdc\x1c\xe6\xaa+\xae\u19df\xb9\x8e){?\xa3#s4M\xeaU&5\x11S\xd8N\xab,Gj\x0e\x80\xd6S\ty\xa5\xaa\x05\xf0\xf9*\xbdAF\xa4Y\xc1K\xe7\xa1\xce5hT\u03e7;\xf1\xc6R\xb9g\x9b\xa0\x18\xeb)\x1fc\xb4\xcf\x1d\xdfU\x00\\\x06\xd0\f2\x803\xa4F\xd8\xf4\xaf\xcfk\xb5\x1f\xc8}\xb0r\xbe>\x1f\xd4\xf6\x82l\xe0\xfb%\f\x1a\xad8\x1a\x921\x12uX\x1c\u03e1\u00de\xa2ie1G\x92\x11\x0et\xc69\xd4\x1d\xe5p2\xcaL2\u0302k\x92\xfa\x8d$\xff3\xf2\xdf]\t\xf3\xcc\xfd\xd4s\xcb\x01M\xfc\xf2\x86D\xd5\v\xa3W-S\xbe\x83Z\x1b\x8c\xf6v,\x826\xbdf]\xbb>\xcb\u0569\xab\xf8h\x97\xff\x9fe)\vsG\x18\x1a[\xc6U\xaf\xfc\u007fX\xb1\xfe\\\xbe\xfb\xe5?a\xcf\xf6{\xd9\xfa\xd0\x1d\xfc\xbf\xefy\x98\x1f\xdc\xf9\x1d\xae{\u00db\xb8\xe2\xd9\xe735\tQ\xdc\xc5e\u067f\x1aP/\xd4*\xdd\xc4\aF8%\x8ab\xe2\x86a\u04e6M|\xfe\xf3_\u099bnb\xeb\x96\xcd4\x1aM\u03be\xe8\x1a\x9e\xf9\xa2_d\xf5)\x97\xd0I\xbat\xdb\v!\u4efa8\x96'?yE\x94\xe6\xaa\x151\x9e\xfa\xb4qE\xef]\x16'\x94S\xa2R\x8d\x12xr\f4\x86\f\xf1D\xe4\xe7'z\xac\x81{\x8f4\xaa\xe7_\xb4RIh\uf234\x92\xf9\xe9\xfa\x0e\b\xdf6\x9aQ\xeb\xbfo&-\xae\xad\x00\xd7d\x9d.\xcd\xc6\x18\xe3\x8b\xd7x\u05be\x90\x05B\x96e\xb4;\xed\xc9\xfb\xee\xb9g)\xc0\xc2\xfc\xfc\t\x11?\xfb\x84\xc0\xfc-o\xf9e\x00>\xf5\xa9\x1b\x9e\xfd\x81\x0f\xfc\u0275\xd3G\x8e\x14Uy\u0357A3\xce\u06b8\x91\u05ff\xe8RN\x9f\xd8K\xb7\xbd\x00\xa2\x85\xf6\x1b\xb4\x00\xe5\xfc\xe22\x81\u05b0\xe2<?nK\xf5\xb4CH\x89\xfc\xe0\x93\x94X\x1d\x11\x19\u05b9P\xadKm\u04ed\xf8\xbe\xe2\x1a3E5\xefr\xb3\a\xa3\xbe\xfa69p+\xbdK8ZQn\xf4zO\u0531\u007f\xf0\x05^\r>.\x979Cwa*\xdby\xf9\x9c2\x1f\xd4\x16^'Z?-\x02m\xa4\xd4Z\x00\xac8\u01a3\x0e\x13Q\x9b\xb5\u00c7\u9e88\u0664\xc9t2\xc2\xc1d\x9c\xc3\xc9(\xd3\xc90\u04e9\xe7\xddS5\x98bh\xac^d\x92{\xe78\xc5%\x0e\xd7\xcdhD\xb6\xbc\xf1e\xf0-\x9c\x1bj\u55a6\xd5\n>\xb7\xa0qF\xc8b?0\xd5pP\x9az\xc0^e\x96\xa1\xb4\x17f\xb0\x8d\x06\xe7\\\xf1\x1a\x96o8\x9b\xef}\xf5\xc3<\xf0\xdd/\xd0^8\xc2\u035f\xff0\x0f>p\a?\xfd\u04ef\xe5U\xaf\xfc\xb7<\xe3\xec5L\x8cCd\x13\xd24)d\x8c\xff'\x02{1\xe4\xec\xa6d\x99\xa3\xd1h\x80\b\xbbw\xed\xe6K\xff\xf0e\xfe\xee\xef\xfe\x8e\xbb\u007f\xf0\x03\x92\xa4\u02d2\xe5k9\u7c97s\ue56fat\xf1:\xe6\x17\xe6H\xb3^gQ-=u\xb4\xea\xc1\x13\xb6\vm\xe4\xf5\xe4\x95\xd9F\xff\x9et\xa5\xbf\x14p\xd6x)b$4\xc6\"\xec\xb0=\x06\xbdBeVt\xb4\xc9\xdb\xd1.\x9c:\xfc\xe7y\xb9\xb9\xec\xd5I\xe9\x0e\x8a\x11\u0730A[\x02]\x17\x16\x87$\u0200S\";\xc5\xf8\xc4j\xaf3\x0f[\x98\xde\xee \u0262\xc8N\xce\xce\xcd]\x04\xdct\xe8\u0421\x13b\x15\xed\xb8`\xfe\xe8\xa3[e\xfd\xfaSTU\u01ef\xbf\xfe\xba\xdf\u06f1\xe3\u0471N\xa7\x83\xb1\x91\xe4\u0546\x15%IS\xc6\xc6\xc6\xf87/\xb8\x8asNZL\xb7;\x1f\xb8\xdc|PG\x9f\xc9S\xfe\x9e[\x93\xf9ac\x14\xacV\xd5W\xe5\xa2\x10k\x86\xa8\xd2$\xf5\x03H\xadT\xf6\xf4.\xd6\xe4K:e\xb5\xab\x95\u0163\xd4\x18\x1f\xcc\n\xc1Y\xd9W\xfa&\x1c2B\xa9\x98\xf5\xa0W\x17ZQY\u0411ZUr\x94J]\x06\xb2\x1b>\xb1\ajUj\xde=\xa3G\xd1\r\u0200\x9bD\xa9t-B\u04e44\x9b\tK\x9a\xf3\xacs\x87ie1sY\x93\xc3\xc9\x18\xfb:\x13\xec\xedLp\xb8;B'\x8b\x83\n\xcd\x0f\x9fr\x1c\xeev\x1c\xdar4\x86\xad\x9fW\x1c\xe5\xa8R\xf2<R)X\xfa\x1e\xcfb\x9cS\xba\n\xed\x8a\x0f\xbaV;\xea*\xf0V\x82\xb0\xb3\xa4K+KY\xb2\xee<\xaey\xfd\xffd\xfdYWp\xe7\xd7>\u03ae-w\xb1\xf9\xe1\xbb\xf8\x93-\xf7\xf3\x8d\u06fe\xc4+^\xfe:^\xfc\xe2\x97p\xc6\xe9+\x19\x1f\x8f\x81 }\r-\xf9\xff)\x1a\xf5\x1c\xc8U\x85F\xa3I7\xe9\xb2o\xdf\x01n\xbd\xf56>\xfb7\x9f\u5bbb\xee\xe4\xf0\xa1\x03\x8cM,\xe6\x94s.\xe7\xfc+_\xc3\xda3\x9f\rv\x88\xd9\xd9#%h\x86*E\\X\xe8\x91\xea\xec#\f\xf9\xc5`L\x8c\xb1q\t\xfeG\xad\xa9+\n\x94\xbc*\x0f\x16\xb7v\xd4\x06\xa7\u0363T\xe5\xc7RR\x95\u00e9Z\xdbVv\xa6%}Z\xfdr\xcde\xc5Z\xce{2\xf5\xb4+\r\x03\xad\xb0\xf2\x9f/\xe08\x05\xb5\x8cN\xae`dl\u048b,\xac\xb7\xad\xb5\xc6\xea\xbe}\xfb\u067d{\xd7Ya\x89'{\xfc\xf1\x9d\xb2r\xe5\u069f\xe8\xea\xfc\xb8\xe1\x14\x87\x0e\x1d\x92{\uee57ukW\xbf\xf5+_\xf9\xc77m\u0672\x05D4\x8a\xe2B\xfb\xe1\u0375\x84k.\xbf\x8c\xb7\xbc\u654c\fY\u04b4\x8b1\x1a\\XC\n\x8f)\xe5\u007f\x0e\xc1\x18%6\x19\xd6(\x1aU<J|\x19\x87qJ\xac\x19\rR\xa2\x8a*]\xfaj\xe9\n\x81\x93\x0fS\f!2\xcd\xff\xfe\xd4X\xba\x12\x05\u0661\xb7\xb5,@\xbf\xc7\xd5@\x82\xda%\a\xf7\x9a[a\x8fk\xa3\xf4\fc\a\u04075\x80\x97\x92 \xaf{\xb5\x14\x9f\xa3\x12\xa6[\xba\xddIe\u05f3\xe8^\xab\x19\x9cU\xffu|74d\x13&\xa26K\x1b\xb3\xac\x18\x9afys\x86\u0278\xe5\xb5\xf3\xea\xe5\x92Y\x98%d\xa2\xa4\x06\x1aC\x96\xe11\xbf\f5\xa8\x12\u02c1\xdci\x19D[\xb8_\x86\x8f\xd4\xc1\\\u2f27{\xaa\xd8\xc4\xdb\xff\xdaL\xa9\xe4\x83\x15\xaf\u007fm\x19)\xbc\x17Y\x92\x107\xc7X\xb3\xf1\"6\x9e\u007f\r#\u32d9=\xb4\x9b\xb9\xe9}\xec\u067d\x9d\u007f\xfe\xf6\u05f9\xe3_\xfe\x85\x83\x87Z4\x9aKh4\u0188\x1bC\xbej\u0574P8\xfd\xffm\u0765!(!_\xf7\x8e\xa2\x18E8tx\x9a\xcd[\xb6\xf0\xa5/}\x99\xf7\xbd\xef\x8f\xf9\xccg\xfe\x9a\a\ueed7\xcc\xc1\xba\xd3.\u44ab\xdf\xc0\xa5/|\x13K\u059dG\xe2\xa0\xd3i\xd7\xfd\xf4\xb52\xac4e\x88\x89\t\x96\u01de#o`\xa2\x86\xf7A\xe9\xd3\x1d\u02601\xb8_\x0e\x8a\fY\uc8c1\xecT\x84\x1d\xb3D\x12t\xe0\x03G\xdf\u04bf\"\xd4\x13\x01V\xa3(\xf3\xeb#\xcb|\u05a6Vh\x91\xdc\u05e4\xd2\xc1z\xe7^\xc59\x9f^\xe5:J\x9a(i\x16\xb6\x8dss\xaaF\x03\xb26{6\xdf\xc9t\x9e\r\n\x18+,,\xcc\xcb\xe2\u014b\xcds\x9f\xfb\x9co\xfd\xd1\x1f\xbd\u007f\xef\x99g\x9ein\xbe\xf9\xef\u007f\xa2\xc1\xfc\x98\x95\xf9=\xf7\xdee\xce?\xef\"\xa7\xaa+^\xfb\xda\u05fcv\xd3#\x8f\x00\xa4\x91\x8d\"#\xa5F4u\x8e\x95K\x97\xf2\xb3/|\x1eK\x17\x8d3\xbb\xd0\xc2\x19A\xa2\xfcb\xcc#\u01f4\xc8\xef\xb3\x01p\x8c\xd12q'\x84X\x9a\\?\x1bj\xbfc\xd7\x18T\x94$\xa59P\x0ejF\xb5\xa0\f\x9a\x95\x9b\xbc!i_\xcb_\xe4\x1d\xf6r\xe5\xa2\x03\xf4\xb3\x14\x1e1\u054d8\xe4\xe8\xecb\x1f\xb4\x84\xc7K\xdfQ\x12n^W\xf1\xcf\xea\xe9>\xd4T~\u007f@\x11\xa9\x01\nEt\x9e\x11e\xd4v\x18\xb1\x1dV4f8i\xe4\x00\xfb:\x13\xeciO\xf1x2\xc9\x117BK#T-MBu4 \x06(\u007f\\\xf9AX\x030\xf17g\xea`\xbe\xeb\x98K\x1c\x99z\xb32\x97o\xf0U\xed\x84+s\x93j\xadX0\xbb\xaat\x17\xe6\u0212\x06\xa3S\xeb\xb8\xfc\xe5\xbf\u0269\xe7]\xcd}\xdf\xfe,\x9b\xee\xfa*\xfbwm\xe2\aw\xde\xc2=w\u007f\x9b\x9bnz&\u03fd\xea%\\\xfa\xcc\xcb9\xed\xb4SY\xbf~\x19\x13c\x06c\xbb\x88\xa4\x15J@~L\x00^\x1a.\x19c\x18\x1a\x1a\x06,Y\x96\xb0\xe3\xb1\u01f8\xf7\x9e\xfb\xf9\xd6?\u007f\x87[o\xfb\x06[6oaf\xe60\xd6X\xd6m\xbc\x80\x8d\xe7^\xc9i\x97\xbc\x94\u0255\xa7\x93e\x8eVk\xde\a\xaaH\x91A\x86:\xa9\f7)\xe8\x95\"\xcd&O\f\n\x15y\xfd\xaa\xae+\xb1*\x11.\xbe\x832!\xe3\xd5\bi\x03h\n\xc3F\x8e\xbe\x96\xa6u\xb6\xbd\xa0\xe3BA\x93\xef\u007f(up\xef\xe3\r{7P\xb5\xfc.\r\xee\xa5\xdd\f:\xed\f\xd7J\x91\xd4\xfb\xf6D\x95\x94\xaf\xa4\xdbab\xd1*\x16\xafX\u03ceG\xeeF\x1b\xc38\x1cVD:\x9d\x0es\xf3\xf3g\xddx\xe3gO\x03\xee\u0773\xe7q\v?\xd9.\x11G\x05\xf3\xbb\xbe\xf7\x1d\xce?\u03db\xa0\xbf\xff}\u007f\xf4s\x0f=\xf4\xf0%\xd3G\xa6\xb1\x91\xb5\xd6V\xc7\x16\x8a5\xc2\xf3\x9eu1\x97\x9c{&I\x96\x90\xe2\u021a\x96\xc8xm\xb6\x84\x05\x1d\u07d3\xfbIvL\x1alUC\x85\x1b<\xbf\xab\xc0j\x8e\x9aI_\xb7\xe9\xf4\xd5x/\x05Q\xfaN\xf8'\x9aa\xaa\xd5v\x0f`\x0f\xfa|\xfd'j\xcd8_+\u0740\x1c\x85)\xd4c3\x88\xc7\xe9\xcfC\x84\x9b\x96R\xc1\xa2\a\x19\x14\x84\x9a\x83\xb7\xeb}\xd4\xf5\xd7+2)K\xe3Y\x16\xc7sl\x189\xc8\xe1t\x84=\xdd)v&\x8b8d\xa7\x10\x1d#u\x06\x1bUm\x05J\xb8uZ\x06S\xf7V\xa4\xdd\f\u6e8e\x85\u0115C\u047cC\xa2\xb6@\xd838\xa5\xb7\xe7.Z\x98,Ih\xa5G\x88\xa2\x06\xcb\xd7_\xc4\v6\x9c\u03f9\x97\xbf\x92\a\xef\xf8{\xb6\xdc\xfbuvm\xbd\x87{~\xf0M\xee\xf9\xc1wX\xb5\xfa$\u03bf\xf0r\xce;\xf7\x12\xce:\xf3,\xce:\xfbTN9e5##\x16#)\u05b8\xe0_\xdf;\xfdx\xe2CT\xe9y\u074d\x91\x90\x16\x1f\x85\u7532c\xc7c<\xf4\xf0#\xdcs\u03fd\xdc~\xc7\x1d|\xf7\xbbwp\xe0\xc0\x01\u04b4K\xdc\x18b\u0769\x17p\u0499?\xc5\x19\x17\xbe\x80\xa5\xeb\xcf!q\x86v\xbbE\x16\xf4\u05e5o\x8f\x94iV\xb9@\xc0\x14\xe1o\x14f\xc8&p\xe4b*Wf=.\xb1\x8f\xeeSBU.>\f\xdcB:li\xc6&\x80\x82\u053f\xa3G~:\x88:\xaf\u0280k\x15y\xf86c,N\xd2\xda04\xf7W\xd7\xca\xf6j\u6105\xaec\xae\xe3H\xda\x19\xcd\xc4\xd1\u0410tU\x19\u00a7i\u00a2\xa9\xa5,]\xb1\xbev\xb5\x87\x82\xdf\x1d>|\xc8l\u07fe\xfd,\x80\xb7\xbf\xfd\xb7\u007f\xe2w\x89\x8f\n\xe6\xf7\xdds\x8fg\xe9TW\xbe\xeeu\xbf\xf0\x86M\x0fo\xf2\a\xb9\x11\xbf\xb6\x1f\x02bS\xa7\x9c\xba~-/\xbf\xf6y\x8c\x8e\x8f0\xdf\xed\xfaTxc0\xa4Xu\x1e\xc8\xc9\x17\x19\x94H\x9d\xaf\xbe\xc3\xe2\x8f8\u05e3 \x19D\xcaI\xed\xfa*+\xf0\xc1\x15pu\xa2.(1Ye\xff\x91\x01\xb6\x9f\xfd\x9f\x91~8,\xe0\xfb\xa9\x9a\xb5>\xe9\xe6_\xeaC&=\xc6\xd7iX\xcb\x14W\xef\x12\xea\xc9Ee\x18\u0148m3\x1auX\u079c\xe1T\xb7\x97\x83,\xe2\x80]\xc6\xfet\x193\xf1\x04\xa9D\xde/#\xf0\x94N\x198Ps\xaa\xb4S\xc7\\W\xe9dZ?g\x8c\xa0qE\x02\xa7\xfd\xd4XM\u03a95c\x91B\xf5\x9a&]\xb2,\xc1\u0688\x95'\xff\x14\xabO\xbe\x84\xf3\x9e\xfdsl}\xe0\x9bl\xbe\xfbk<\xb6\xf9\xfb\xec\u067d\x99=\xbb7\xf3\xb5\xaf\xfe\rk\u05de\xca\xc6\xd3\xce\u1b33\xce\xe3\xb4\xd3O\xe3\xb4\xd3N\xe5\xe4\x93\u05b1b\xf9$\xe3c\x11qD!\xe9)\xaaU\xc9\x13e\xfay\xef\xf29\x19\xeaf\xa3\x8e\x03\a\x0e\xb0e\xcbV\x1e~x\x13\x0f\xfe\xf0!\x1ex\xf0\x87\xdc{\xef\xfd\xec\u07bd\x8b,\u02c8\xacedl\x82U'\x9d\xcb\xfa\xd3/c\xdd\xc6KY\xbc\xeat\xc4\xc6,\xccuH\xb3\xcc\x1fzZ\xd3y\x96\x9d\x9b\x04\xaf\xe1<K\x17A4\xb8\xf4\x05\xf3\xaczoS\xbf\x92u\xc0b\x99\xb7\xb6\xf5FZ*\x905\rCc\x11c\xb1\xc1\x86\u074a>\xab\xba|o\xa1\xd7\xee!7d\xab\xf8{\x9b\x9e\xd3Z\xfcp\xccW\x02N\xea\x93\xd0\u028f\xcaTi%\x8e\xd9VF\xe2\xbcL7\xf76\xd7\xfc\x1a\xcf\xd4o\x8a\x8a\"&f\xf1\u04b5\x8c\x8c\x8c\x91d)Q\x14\xf99\x9e\xb5\xb2g\xcf\xe3\xcc\xcf\xcf]\xa4\xaa\x93\"2}B\x82\xf9\xe6\a\xefd\xe3\xd9^\f\xffG\u007f\xf0{/\u007f\xf0\xfe\a.\x99\x9b\x9f\xc7\x18cLem\x1f\x81\xd8Z^\xf2\xfc+9\xfb\xec\xd3h\xa7]2\vj\fq\x90\x0fVR\x12|\xc6e\x05\xc8sS)\xe9\xc9\xc3\xe9\xa3\"jU\xb8\x1c\x15\t\xb5oPW\xd5>k\x1f\x84\xf4\xd6\u294b\x9c\x1es\u03a3\x95Fq\x90\x19m/\xe4\x1d\xebsOk\xe3_\xf0\xa9Z\x13\xc4\x14\x80\xdes\xf3\xe4\xaf|$\x8e\xa9x\x81)\xd3f\x9d\x1cb:y\x9c\xddf9\xbb\xa3\xe5\x1c1\x93$\xc4\xc1\xa3\xd9U\xba%\x0fz\x9dLYH\xfd\r\x98\xb8\xf2F\xae\xfe*'\x12\u031b\U0008391e\xd4R\x02\x98\xf6(\x93\xa4^\x14:%u\t\xe9\xcc462,]s6K\u05dc\xc5i\xe7_\xc3\xe3\x8f\xde\xc7\xceG\xfe\x85\xc7\x1e\xbe\x83\x03\x8foe\xe7\xceG\u0631\xfd\x01n\xfb\xfa\x17X\xb2l\x15\xabVm`\xf5\xeau\xacY\xb3\x9aSN\xde\xc0\xa9\x1b\u05f3j\xd5r\x96-]\xc6\xc4\xe4\x14C\xcd!\xe2\xc8\xd2lZ\x1a\x8d\xa8\x06\u058a\xd2\xedt\x99\x9d\x9d\xe5\xe0\xa1C\xec\xdf\u007f\x80\u077bw\xb3}\xdb\x0ev\xec\xd8\u03ae\xdd{\u063a4\xa7\n\x00\x00 \x00IDAT\xbe}\a;v<\u0291\xc3\a\x01\x88\x1bC\f\r\x8f0\xb9d5kO\xbf\x84u\x1b/e\xf9\u06b3\x18_\xb4\x864\x83v\xbb\x85s\xed\xf2\xc2\xcez{\xa0\xb0OQ\x84/\u7b85\x06\xa3>\xecA\x8c\xc5`\a\\Ez\x9c^\xd1'e\x15Uy\xc3\u0418\x88\x19i\x1a\x1a\x86b~Q\xab\xf0\x85\xbay\u0500\x8a\x9c\xaa\xc1\x9dT\xa6\xdc9\xa0\x1b\x1f%\xa9d\x15)n\u0391\xfb\xfflg0\x9fx\xa5U\x1e\xfc,\x15\xea\xd0\x05\xff\xfc\\\xff\x9e\xa4)K\x96\xaebjj)\xbb\xf7\xee&\x8a\xe2B\xf2\xdciw\xd8\xf9\u062eE\x0f<p\xdf(pb\x82\xf9\x1f\xbd\xff\xcf\xfc\ub9fa\xec\xf5\xaf\xf9\xb9_zx\u04e6\xd0R\x9aR\vm\x84$I\xb8\xe0\x19g\xf2\xfc\xe7=\x8bh\xb8\xc9\xec\xfc\x02\xc6X\xef`\x18\x8c8\\x\xb7\x8d\v\x1f\xd5w[{\xe5}\x955\x9d\x9a\x87\xb9<%\xd4\xd3\xda\u0630\xce\x16\u66d6\x1a8e\x82l\xce\xf4if\xfb\x97\x84\xfao\x1b)\x8e\x05\xcf\xf2\xe7\xbf\xd3T\xf4\x1f!7T\xa4/\x00\x8f\x1f\x85\x8e9\n=\xa3\x15\x11x\x11y\xaa\xbd\xa7\x99T\xdad\xdf^\x8fH\x87\x11\xf6\xb3\xa4{\x84\r\xe9n\xf6D\xcby\u052cd\x0f\x8bHL\x8c\xa8\u00d2\xe1\x1c\xb4\x12e.qt\xb3\xa0\x00\xaa{y\x15\xb0\xa4\x86\xb0\x02*a1\xacR\x98\xd5\\\xf4\x82\xb9Y\xcf\xeb\xa3Tl\x11\xf2\xf4\x9b\xd41\xd7=\x82\x901>\xb5\x8a\u0265\xeb9\xe9\xcc\u02d9\xbdb7\x87\x1e\xdf\u009em\xf7\xb0{\xfb=\x1c\u07bb\x8d\xb9\xf9i\xee\xbf\xffv\xee\xf9\xc17\x111\x8c\x8eN2\xb5h1\x93\x93\x13L\x8cO2>1\xc5\xc8\xc8\x18CCC\f\x0f7\x19\x1e\x8ei\u013e%hw\xba\xb4[m\xe6\x17\x16\x98\x99\x99\xe6\xf0\xe1ifff8r\xf80\a\x0f\x1c$I;\xa1\x03\x89\x19\x19\x1bg\xe9\xea\rL,Y\u0272\x93\xceb\xd5)\xe7\xb3x\u0169LL\xacfhh\x8a4I\x99_\x98\xf7^#b\xe8[\xe6\xd1r\x10\x9e\xf3\xc2B\x1e}\xe8kT\xa3\x12\x92\x82\x82\xb7\xbd\x1bTvT\x87\x90\xd2s\x9d\x86n\xcaz\x19\xb0\xb3\x06;\x161<j\x89\r\x03\xc9\x19\xed)D\xa4\xd2(\xeb\x00&T\xfb.1-\xa6QbL\xbd\xab\u00ebVP?8og\x1ab\xe0\fY\xa6\xc5,\xa8\xba\xbfP\xca\u05c5,I\x19\x1d\x9dbdd\x1cuY9\x93Q\u03db/Y\xba\xe49;w\xee<\x03\xd8}B\x82\xf9\x87\xff\xe2\x06\x05\xf8\xf8\x87\xde\xff\xb3\xf7\xdd\xff\xc0E\v\xadv%\x1d#\xb4[\xaa\x8c\f\r\xf1\xe2k\xae\xe0\xd4\xd3Nf\xb6\xdd%\x8d\x9aEE.\xb9\xfdN\x00q\xa9\x82\xcb\xc0\xab\xa0\xa2Q\t\x91f\x88<A\u042e\x03B\xee\u0218\x03\xa7\xe9\x19\xd6(\x90`\xc9B\x8c\x9b%\xf3v\x005S^z\xa6\xf2U\xc0\xaev\x98\xf9W\x94\xc0\x9d\x8fl{)\x19\x11*|{\xbd\t\ueb45\x06\xc5\x06\u0213\x04\xf5\xe2\x97:-\x8d\xb0\xb4\xda\u0754RN1AQ\x122\x15\x1b$,\xcb\x0e\xb3\xc8\u0370F\xf6\xf0\x98]\xc1\x0e\xbb\x9a=Lq$\x8bh'>X\xd89\xad\x9d\xb5\x03;\x19)\xa3O\xeb<uYa\x15\xaa\x8d\xda\u0560\x95\xe3\xbd\\\xac\xd2B\xf5\xe0_\xf3vk\x014\xc3F1KV\x9c\xc6\xe2\x95\x1b\xd9p\xd6\xe5t\u06f3\xcc\x1c\xdc\xc5\xc1\xc77sh\xcff\x0e\xef\xdd\xc6\xec\x91},\xcc\x1dazv\x96\xfd\a\xf6\x93t;\xb8,\xe1\xe8A\r\xbdG\xae\xc16\x87h4\x87\x18]\xba\x9c\xa1\xd1qF'\x97\xb0x\u0769,Zw*\xcbN>\x83\xc5\xcb\xd6\u04b0c\x183\x02\xa9\x90\u0336\x98\x999\x12\xb6\x1eM\xbd\tQ_\xfbK\xd1\u0494\x85\x8c\xe4\xe1\xaa\xc1,\xcb\xe2o\f\ttK\r1u\x90\xba\x8b~\xff\x15\xbc\x141\vC\xcfh\xc4\u041c\x88\x88\xad\x10\x99\x90\xe5J\x9f\xfbN%\x01\xb1'\xc36\xbf7\xf2\x9c\xdf\xda\x1c\xaaz\xb5W\x12w\x8cx\x0f\x19-\xfdvT\x95N\xeaH3\xafV\xc9[\x15\x13\xd4m\u04ab\xce\t\x85\xa1K\x1c\xcd\xc6\x18\xf1\xd0\b\xe41\x95\xe1\tdiF\x96ef\u07fe}\x8b9\x01\xfe\xf4\x81\xf9G>\xfc\xa7\xf2\xe6\xb7\xfc\x8a\xaaj\xf3-\xff\xe1\x8d\xffq\xeb\xb6G\x01\xb0\u0592+X\xac1t\xbb\t\x17^\xf4\f\xaey\u03a5\x18\x11:\xce\x12\x19\xa1\x11\xf4\xe0y\x80\xb1uZ;\xb2{\x17_T\xca1e1\xc8|\x92 \x9e\xff=\xc3\xd0\xc1\x1b\xdeG\xb8\x02\xa0\xb5OR\xe5\a\xb3\xb6\x10\xc3y_\x13\xd3\xf3\x95\xda\xc3<\xf6\x91%\xa1\xa2w\b]\xe2`1\x90\xd5\x06\xb7\xdeU\xc6\x06=\xbb\x1b \xab\xa4\u03d6T)Ez\x83\x8e<\x8e\xf3\xb9~0\x95\xb0*->\x86\xab\xfao\x96\xc2\xc9N\x8d\x9fi\x94\u06f4J\xa4\tK\xdda&\xb3Y\xd6\xcbn\xb6\xb2\x8c\xfb\xb2\x95<\xea\xa6\xe8h\x03+\xfe9\x17G\x9f\xd4+\xcd\x1c\xc4]8\xefr\x83\xaf\x1c\x99\xb52\xf8\x10\xad\xce?\xfdB\x99\u04de\xbc\x8c\x9e'\x9dyS\x11\xefS\x9fet\x16\xe6\xc0\x18\xa2x\x98\xf1\xc9q\u01a6V\xb3b\xc3\x05\xa0\t.Kh\xcf\x1fa\xe6\xe0.f\x0f\xef\xa55\u007f\x88\x85\xb9\xc3,\xcc\x1e\xa6\u04de'\xe9\xb6H\xba\x9d`\xd5\xec\x13\xb2\xc4\x1aL#\"j6i\f\x8d0<1\xc5\xc8\xe2e\x8c,Z\xc2\xd8\xe2%L.[\xc5\xf8\x92\x15\xc4\u00e3\xa8X$\x8a0\b\xe9L\x87\xee\x91\x16\xddVB\x9af%\u0329+\x02\u0335\xb7\v5R\xef\xf5\x82<\u0408\u0146\x94)\x0ft\xe1\xea\xecuZ\u8ece\xa4\xcfD_E\xc8bCf\r\u0450\xa1\xb9(&j\n6\xa75\xa8[\xd9\xf6\xa1\xb9\xd6\xff.}\x95\xbfV\x92\x8d\x06\\\x9ba\x05Z\xabn\xbd\b]\a\xad\xb44\xb6\u00c8\xb7\x98\u02028\"\xffy\xc1\x8c&\x97j\xaa\u02c8\x1ac\u010d\xd1zL\x1b\xbe\xf8\u073b\xf7q\xee\xbf\xff\xbe\xe4\x84\x04\xf37\xbf\xe5W\x14\xe0\xbd\xef~\xe7\xd9\u07fd\xe3{g/\xb4Z\x1e\xccM\t-\xce)\xe3c#<\xff\x8aK\u0670~-\xfb\xe7\x12\x8c14Ih\x90\x16~)\xb5\xe4\x13\xd5\xfa\u025a\v\xb2\xf3\x05\b\xe9+\x04\x8e\tR\xdaC\xa5\xa4\x18\xba\x1a\xd1\x15KLV,\x03U\u056b\xd6\xd7@\b\x10\x91\xfa\xea\xbdpI\xac\xfb\x0f\xea\x00~{\xf0c\xf2\xdfms\x97\xa0\xa2\xa1\x84|\xe3\xd5\xd4l\xbd$\xac\xda\x1c\x9bb\xe9\r\x0e\xd0\x01\xdc\xfc\x13\x16\xdb\xe5\xb3\x06\x13\xd2\xd2+\u06e7\xb9'Mi;\xa0\xb5\xda\u0285\x1e\xcbi\xc6X6\xcb9\u0332\x81\xddl\x91\xe5\xfcPV\xf3\x98.f\x8e!\x9f\xfc\xa4Y9\xbb\xec\rL\x92j\x958\xe0Y\xea`2\xab\x8c\x9c\xd4\x1e\x85F\xf8y\xe1\u07camT\u007f\x81\x92t\xdat\xdb\xfe\xf7Yk\xbdEj\x1c3\xbax\x94\xd1EkX\x1dN\a\t\x8f\xcb9\xc5e)Y\xda%s\xa9/\x88\u0160V\x90\xe1\x88hx\x88\xa8a!\xb6d\xc6'*EQ\xa9\xc5vY\x86f\x19Y\xda&I3\x9cq\u0210b3AS!u\x8afZ\fX=X\a\xf0\n\x87h\xe9l,\x15P\xb3X\x13\x87\xcc\xce*\xb0J\x9f\xda\xe8h\u05d4V\xba\x1cg\x84\xcc\nQShN\xc5D#\x16#\x14`\x9eW\u0245+\xa8\xf6\xf0\xe3U\xfd~\xcf$\\z\x93+zd\x04B\x99PU}/3\xf5@\x9e\vy\\\x00s#\x82\xc9*\u0770\n5\a\x8e\xd0\xf1;bT\"\u007f\x8dT\x8bG\x81\xf9\xf9y\xf6\xed\xdb\xcf\t\a\xe6\x9f\xfa\xd4'y\xc3\x1b\xae\x03`\xdf\xfeCo\u07f9k\x0f\x99sXk\xc3p#7\xd3J9\xf7\u0333\xb8\xe6\x8aK\xe8\xa6\xfev\x1f\r@nU}U^\xbb\x93\xfbU)\x1a\x8c\xf0\x9d\x19\xdc\xdc\x1e\xab\x12\xad\x02\x9c\v@\x9e\xe1}\x1c\"\xf5qryU<\b`\a\xc1q\x17S9\x00\\52\xf9\xa8\xdd@\xfe\x15&\xa8eJ\xbe\xbc\x94n\xda\xf0Y\xa5\xee\x8dq\xac\x01\u0560\xdf*\xa1\xf3\xc8B\x0f!\xc1\xa3f\xb0\xf9\xed\x80?\x86P\x05k\x1f\x87Z\x85J-8o_5\xbb\xa0\xf9\xd5J\x8b=)m.\xe6QNc/[X\xce&V\xb2C\x970-\xc3\x18\xbcZ\xa96\u0516\xea{\x96k\u0635\xae\x8d\xcf\x1f\x912\x80n\xa1\xd8\x1bp\x85\x9e\u065fD\xe2\xfc\x01\xed\r\xd5\xf2MBW\xbe\x8eNq\x9a\xe0\\\x82K\xc2s\x0e\x89\xee\xd6HI\xbd\t~\xf1&\x1e\xf6[\u00a2\xb8,|}*\xe8\x02tZ\t4\x12\x88\xc0\x841o\xea\x1c\xear%\x87\x16^1j\x05F-\x91\xf5\x1d\x86\x11p]G\x9a\xfa\x9f\xab\xf9i*ZZ4Ky\xb5J\x11\xb5fJ\x1f\xf2Z\xf4\x9f\x1eC\u07a4\x03\x8a\x1e\xff\xebR\v\xb6!4\xc6-f\xcc\xebD\x82\x1d\x12=\xfbp\xa5\xc3\xf31\xab+\xad_\xd7rl\u05af\xa6p\x14A\x9d\x1f\xa2w\xb3\u04b7\u0204=\nu`\\\t\xfe\xf9\x82b!\xcc\x14\x83:o\xbcU\xdd\xf8-\xe7EB\x969\x92$=\xf1\xc0<\a\xf2n\xb7\xb5\xf2\x85/|\xd1U\v\v\v\x9e\xc26\"\xa5\xb63crb\x8c\xe7_y)\xeb\u05eeb\xba\xd5a\x98\x94\u0607\xa4\x15Ugq\xba\x17\xc5q\xb8\tM~sW\xe4jO`\x188\xc8\xcaU+cF!\u00c8\x90\x15\xd3\xfdrE\xc2\xf4\xd4\a\xf9\x1a\xba\xf6\x1aF\r\xf0_9\xba\u0250\xf4\x18\x87U\xb74\xb5\xa7&\xa9\xd7\xd7R\xfb}G;&\x06\xcf\x04<\xa5\xe3\x87a\x16\r\x1d\xc1\x13\x1c\x9e\xf6\xe53\xd6o\x00\x97?\xa2\xf0\xc68u=L\xb2G\xda|\xc0;N\x87\v\xe41Nc\x1f\x8f\xcab6\xebr\xb6\xb1\x8c\x83\x8c\u1430\xb5\xab\xc5T\xa0\\t:\u01aax\xcd\u0295\x92N\b\ufa69\xe4\xa9J\xde\xd9i\xd9z\xbbJ&i^i\xbaj>\xa67\xf6\xf0\xfbiR/2\x8b\xdf\x19\xac\x94]\xde\xdew\x02\xfdg\x05\xed(f\u0220\x8d\x88,\xb6%\x90\xe7\x86i\xb9\xd41\u007fZC\x06\x91\x88H|\xe1\"\x89\xa0]G\x96iPrI)I\fT\x8b\x02V\xbc$\u03c6\xf7z\xe0\xcaE\x0f\xf8\xd6\x05\x05\xfd.Bi\bgn\x8cZ\xcch\x14\xf6;\x14c\xfd\xe1\xe1(3l\xa5\x17\x82\aJ\xcc\xfa\x03(\x8ev\xe7\x94~\xa3\xf5qY\xa6\xd0I=\xfdc\xaa\xc3U\xc1W\xe5\x15*\xa7W\xb5\x9c?\xc6j\u982a\xb5\xd7 \xcb2\xba\xdd\u0389\x05\xe6_\xfb\xdaWy\xc1\v\xae\x05\xe0\x03\x1f\xf8\x93\x9f\u06fcy\xcbD\xab\xdd\xc6Z#\xa5\x1cQ\u025c\xe3\u0513\xd6p\xcd\x15\x97x\xbe\xca%\x05\xdf\\\xb1\xaa\xa6w\x9b\xba\xba\xf4p\xd4\xd3Z\xfb\xa9\x96\xa3\xa1\x92\x14f\\\x8a\xadU\aYm\x01Hj\xff&\x94A\xf5R\x8c-%\xf0\xe7\x1c\x0fd\x8eR\xf5T\x0f\n\x83\x1bx\xf0\xc8\xc0q\xef\x13\u05ea\xfb\x0e\xc0\xd1\b\x9d@\xb9\xb2e\xca\x03\xf4GT\xc3\x14\xac\xc51\xd46\xd5\xe5\xa9\xfc&\x1a\xa1\xcbY\xec\xe6$9\xc0>&\xd8\xc626\xb1\x82\xdd:EBDD\x86\x13Wn/\xf6\r\x97{\xdfs\xad\xad\xc5he\xde\"aP\x9aW\xebN\r\xc6e\x85iZ\xd1\f\xaa\x92U\xf0E2-\xbeW\xaby\xabT6X\x1d\xe5\x82VX\x1d\xaf\xea\xe3\x9d\xf3\x9f\x88\x1a\x82f\x12\x12\x90\xca\x1fT|\xbd\xd6\xdfmi\b:i\xb1\r\xc1\xb4\x14\x8d\x1cY'C\xb3\xde!q(\xe1\xadE\xd5\xfa\u047d5\x85\x84\xb3\xda9\xf5]\x1c\x85LE\xfa.YU\xbc\xb5\xed\x90!\x1e\x8f0\xe3\x11&\xf2\a\\^\xfa\xb8^)kM\xf6$\xf4\xa6Ni/\xf1%\xbdc\xd3\xd2\ufa38\xb6D+\xc0\xeb?:\x99zI\xab\x8a\x1f\xc2W\xc0>\xe7\xcb{'W*\x82q\x14IK\xfe\xec\x1d@A\x1a\xc19\xf5A\x1e'\x12\x98\xe7@\x0ep\xf7\xddw\xbf<I\xba\x11\xa1\x95)NQ\xe7h6c\xae\xb8\xe4\\\u05af^J\x96t=?.=\x9b\x93=\xc3\x1c\xe5\tJ\v\x9f0\x12i%\xb2\xd0\x15\xaa\x06\x1b\xfe\xe6z\xb7\xd7\xfa\x86\x8d\xbd\x8a\x95\xfa\xb0P\xf3)z\x1fD\xeaQ\xf9\xfb\xaa;I\xef\xe8g\x10\x90?\x19\xe0\x95>@w=u\xfe\x8f\xcf\xe1S\x8e\t\xea\x9e\xffw\xc1\xad\xa9!)'q\x805r\x98\xb3\xd8\xcdfV\xf0CV\xb1\x87I\xbab\xc1@S\xd2\u00b7\x9d\x819IZToZM\xf2\x93\xbaJ\xdf \x181\x1e\x8d4D\t\x86.E\xb5b\xbd\xaa\xe5 Zz\xd0H\xa4N\x03i1\xa9\xf5\xc3\xe2\xda\x01\x13\x02ql\xe4\xaf!\x97\xa9\xa7\x00l.\xbf\u0492\xcf\x12WZ\xb4\u57cf\f\x8c\xfa\xb9E,\x8aSC')\xb70\xfc\xf4\u0440\xf5\xb4J\xbe0=h\x06Y\xbamV\x1e\x9f\xe4\x8b5\x15\\\x95\x12\xdf\u0350\x10OX\xccT\x04\xc1#\xdf\x1a_\r\xbbL\xc3\xfc@\n\xb37=j\x9aEO)S\xbc\x9cR(\u0574\x06\xe8\x15\xe1\x83+\xfd\xcd\xf3\x85\xc3v\xea\x818\u03fa\xcd\v4\u3d28\xcc\xcb\x01\xb9T\xde3\u03df#~\x86\xe7\xdc\x13\x13J\x9cP\x03\u043d{w\xaf\xbb\xe6\x9a\x17\x9e|\xf0\xe0!O\xb1\x88H^\x95\xbb\u0331~\xfdj^\xf2\xbcg\u04b0\x86\xb9N\xb7\x06\xe4\xbdn\x88G\x83\xab'\x10K|||\xaf-\t\xd6o\xd5^H5}U\u07e0){\xce\xc3\x1e\x8dq<\xca\x05\"\xe5\xf0\xae\x94n\x1d\xfd\xfb\xe4\x18\xc7\u00d3;\xef\xb4\xe6\xb5\xf1\xe3\x02\xf2\xe3\x01:\xa1S(\x80\x03\xbf0\xb6\x86#,\x939\xced7\xdb\xec2\x1e\x8eW\xb2?\x9d IlH+\xcfz\x16e\xfa_#Wl\x83j\x9d&\ntJ\xae\xc216\xf6\xab\xe2\xaeLF\xf7\x9c\xbf\xab\r\u07b5\x8aR=\xd9\u01c5\t\\@\xedb\xd2R\xb1\x18\xf0\u0671\xa6\xb4X\x90\xd2\x18>7\x8e\xd2\xde\"\"\xb7f%$h\x8d\b\u0608\x86q\xd0vtSj\x9d\xa3*H\xe6\x02p\x95\xa7\x99\f\xaa\x9b\xfb\xf60*:\xfe\xd0\x01;\x01\xd304\xa6\"\u0322\x18m\x94\x12!U\x0f\xa8\x05\u0154SX\"\xd8 U\xad\xf2\xe5\xf9 \xba\x06\xd0E\xaeD\xa9\xae\xa9\x96Py\xc5o\xf2\xb9G\xe8\x96R\xa0\x9d:\x12\xa7\xc5<$\xa7]\x14\xc1\xa6\x0e\xeb\xf2\x83\xd8o5\x9b\xe0GcB\xf9\xae\x94[\x1cZ\x9f\x0e\xf4w\xfc'\n\x98\xdfy\xe7\xf7\xb8\xf8\xe2K\x01\xf8\xf4\xa7\xff\xd7\xcb\xe6\xe7\xe7\u05e5i\x8a\x91\"\xaf\x06uJ\x14E\\\xfc\x8c\xd3\u0638a\rY\x96\xf9\xb0\x01\xaaaaz\\\x84>\x9a\xdb\u02a0RU\x9e \xd8\xf4%\x81\x0fPz\xc8q`\xb3\x1aKE/\x1f\x9fK\xf7\xd0J\xa8CUc\xab\xc1\x97\x9dB\xae\xd5ov4\xb8B\u007f\xd2M\xc9q\xe6\n\xfa4\xfc\xcc'W\xa1SL\xcb\xf2\xae\xc7\xf9\t\x06\x06\xc7R\xe6Xd\xe69ed\x1f[\xcdr\xb6\xe8\n\xf6\xbbq\xda.\xc6\xc7g\xb8\xa3\xf4?A\xf7\xa3e\x17&Z\xe5\xfd\xb5R\xd6z\xf9\x9eD\x06\xa31\xaa.|(\xea\xb2|\xc70\xd0$\x01\xb0\xabUs1q\xd5\u06ba\x8d\xa1\n\xfcBd\xfc\xd0TB\xe0I\xf1\xb5\xd2S\xa9R\x82{\xfd\x00\x0f\xb2\x91\x91\b\x89\x95\u01bc\u00f4\x1c\u074e\x06P\xcd/.\t\x83\xe0 &\xb0G\xa9\x8e+\x06s\x85\xc2\xc5J\xe1u\xae\x026\x16\x1a\xe3\x11vq\x03\x86\xbc\x99]\xeec\x8f\xfa\xeb62\x82-\xb67\u00dd\xac\xa1\xeb\t|\xa9Sj\x96\x0eE'\x13T8\x92\x1fD\x94A%U;\u072a\xa7\x8a\"\xdeD+\v\x11\x8f\x15jJ\xc5\xcf+l\xe2\x15q*\xf9\xe6x\xa0\\\x8c\x0f\x06\xafJ\t\x9cs\x14\x9cU\x8f\xd8\xc6\x1aK\u0708O\x1c0\u03c1\x1c`\u01ce\x1d\xcf\x1111\xd0\x11#\xcd\xfc\xc6r\xea\x18\x1d\x1e\xe6\x99\x17\x9c\xc5\xc8P\x83v7\xa9\xe7\a\xa2\xc7<\x05\xf5I\xa0\u0493\x01!\ud66bs\x94\x15\xfbz\x04\x9c\xd6\xf3@\xc5`m\x8c\x8db\xd4Z\u007f\xc3\xe6\n\t\x97\x91e.\xe8\xeb\xfd\r\x1eE\x9682a\x131#I\x13\xb2\xa4\xeb\xdd\xee\xe85\xa8\xd2\xe3\xd6\xd0Oy1\xe88\xac\xbe\xf6\t\x1c\x9f\x9eJ]\x8f\xfa\xde\xf5\x1fay\u03f4\xc4\xcc35\xbc\x83S\xe2\xfdlm,g\xd3\xec*\x0e\u034f\x92\xba\x88X\xb2\x01A\bU\xa9\xa2T\xdc\xfa\xaa\U000b6cbc.L#\xf2\xb4\x1d|0\xb5\x1a\x87\xe2\xc1]\x8c\v\xc3M\x17\xec\x85u\xc0u+}v\xc2\x1a\xacQ\xac\t\x8bs\"X\xe3\xd7\xe9\u0542f\x0e\u056c\xa0A\\\x95\xc8\x13jFi\x84\x8a\xdb4\r\x1a\x19\x88\x1d:\x9f\xe1\xda>\xd72\x1f\x14kF\xcd`\xabj\u00ec\x15]zAg\x86\xf0\x12\r.\x95*\u07b1\xb21\x12\xa8\x95\xe1\xf0\xaa\x99\xca\xd05,\xa2J\x1e~R\xe4\xf8\xfa\u1dc4\xa1\x83J\x91RW\xd0R\xd2+=\xad\xddo\x95\xc0\x89\n\xaek\xb8\x17\x12\f\v\xa9w\xd8\xcc\xd7\xea\xf2 \x18\xf0rD*4\xb78\xf1\xea\x9d<0]\xa4\xa6\xfc\u0272\x94,K\xfb:e\x14\x8c54N$0\u07fau\xb3\x9cr\xcaFU\xd5E/z\u044b\xd6\xed\u06b5\u02ff\xa7\x95\x8aC\x15\x16OMp\xc6\xc6\xf5\x18\x1b\x93d\u0770\xf03\b6\x8e\x0f\xe0G\x03\x86\x1f\x1d\u0334\xe6\xb7Ru\x13\xf1'\xb5?\xad\x8d5DQ\x8c\xb1\x11\x9dn\xc2\xdeC3\xec;x\x84#\xd33\xcc\xcc\xce177\xcf\xfcB\x9b\x85v\x9bN'\xf1\x12M\x11\x8c\x11\x86\x87\x1aX#\u0111e\u0572\u015c\xb4n\r\xa7lX\xcd\xe2\xc9q\xb2$!M|\xa4Y\xear\xdd\xeb\xb1\x0f\xa2\xbal\xf1\xe9\xeb\t\x95r'O\x8a[\xe9\xe9\x01u}\x92\a\x80\vJ\x94%\xf1\x1c\x8b\xc6\xe7Y\x1b\x1fbs\xb4\x82ms\xcb8\xd2\x1d\xf1\x8e\x8e\x15W\xcbz\x180T\xa7\xa0J\xff\xa0</\x12\xfb\x1e\x9b\bF\x82\xf9\x12\n6\x97\x0f*\xaaY\x00\xfa\\1S\xfa\xad\xd7|Hr:'\xaf\u028dE\xa2\x18i4\x10\xabh\x9a\"\x89\x16\xdd@_R\xf2\x806P\x03\xdfnF\rqC0-Cw!#\xed\xf8\u007fs\x86RSm(*\xd4\xe2\x8f\xf1&Y\u0396\ubd5a[\x17\x1b\x0f\xe4\xcd!C4\x19!\xa3\xb6b\xb5\xe0\x03`Ll\x8a\b:\xa5\xaa\u007f\x0f3T\xa7%HW<\x91\u051f\x85d\xc1\xee\xd8U\xc1<T\u84bf\x8f\xe2A$2\xa5\n)Ue!\x81$\x1cj\x1a\xba\x03\t\xd6\u02a0H\xea\xe7\x11\xfe\xd0\xf1\x15z>\x9d\x17\xcdu\xf9\xf9\x90\u04d0t[\xa4\xddN\xe9\xe3RI%n\xc41\xa3\xa3\xa3'\x0e\x98\u007f\xe7;\xdf\x05`\xf3\xe6M\xe7\x1c:th}\xbb\xdd\xc6\x18cD\xeaV}g\x9e\xba\x9e\x95\u02d7\x929\x17\xde\u0132*\x10\xd1\xe3\x80\xf8`A\u078f\x02\u40f2~\xf2P4W\xf9\x00\xc1F\x96(\x8a\x88\xacg\xd1\xf7\x1d<\xcc#[\x1e\u246d\x8f\xf2\u062e\xc7\xd9\xfd\xf8~\x0e\x1c>\xc2\xfc\xdc\x02s\xf3-f\xe6\xe6\x98_h\xe1\xb2\uc60fa|l\x94\x95\u02d7\xb2v\xf5rN;e\x03\x97\x9e\u007f\x16\x97]|.\x8b\xa6&1Y\niB\xb7\xdb\r9\xa9\f\xf0y\x91\x01\xa0\xaeO\x19p\xab\xf3\x02W1\x1b+f\a\x05%\xf2\xa3u\x02\xf2\x14\xe8\xc8\xfc\xbd0(\xab\xe2#,Y4\u01c6\xe6\x016\u03ef`{k)s\xc9P\xe0\xa5]9H\x13\x1d\x9cC\xa9\xd4\U000a628d]\xa9S%9x\x16\xda\x18\x01c\xf3OG~\a@\u0577\xf7\xf9\x81\xe1\xb4F\x91\xe4\xbc-\xc6gk\x9a8\xc2\xc41\xa6\u0440\x18\xe8&\x1e\xc8]\xeaW\xca+A)\xc5;*u7I\xadX\xdcJl\xb0\x9104dH\xdb\x0e\xed(\x9a\b\xa6k\x82[b}8_X\v\x9bJi,e\xc5j\x8d0\xdc\xf49\x9ef<\xf2\x86]\xf9\xb0\x90R6\\\x04\x82x\xb5fy\xd0T\xa8DU%\r\xbe)i\x00\xfa,\xfc\xb7+R\x81\xca*\xdd\u4389\x15\xf9\xa0\x15\xc1\x86\xa7\x91*\xdes\xa5\xe2K\xae\x95\xca\x1d\x85(\xf5\x03\u043c\v\xab\x9a\xf2\xf5\xe8\x9d1b\xe8\xb6\xe7\xe9v\x17JW\u01fcCSel|\x9cU\xabV\x9b\x13\x883\xff\xbe\x00z\xf3\xcd7\xaf\x9c\x99\x99^\x1c^\x8cZ\xf3\x1cG\x96\x8b\xce=\x83\xa9\xc9\t\xda\u0764\xa2\x1a\xad@\xb4\x0e\x8eR\xeb\xd7w<\xb5\xe1\xdf\xf1i\x96R.\x97/\xd84bK\xb3\xd9\xf4i/G\xa6\xb9\xff\x81M\xdc\xfe\xfd{xx\xf3v\xf6\xec\xdd\u03c1\x83\x87i\xb5;\x81:\x89\xe9&yjzV\x02p~\xe1U\u03efp\xa1\xcf\xce\xcd3;7\xcf#[wp\xeb?\u007f\x8f\xcfNNp\xea\xc9\xeb\xb9\xf8\x82gp\xcds~\x8a\xf3\xce<\x99\xb1\xd1Q\xc82\xda\xed6Y\x96URu\xf2g`zh\xa1~\xc5\u0353\xedN\\\x10\x8c\xd6\x01^C;[7Q2uB\xe3G\xe2\xec\x8f\xfd\xb5\x95\x90k\x11\xaf~\x199\xc8\xf2\xe6,\x1b\xda\ayxv\x15\x8f\xb6\x96\xd0u\x91W\xe8\x19W\xb1U8\x96\x8c\xb1\xfeI\x95\x9a\u0322\xb6\xd2^\xd2\xcd\xc1#Dm\x19,\x9e\x9f \x01$\xf3\xed\xd0B\x06n\r&\xb2\xd8\xc8bl\xe4\x95'\x91\xc1:\u023a]\u0124\xe1,\xa8\x9a\xbbU\xcaE\xca\u02bd\xd8\x00\xd5\xd2\xfa\xd64\f\x8d\xa6\xf5\xdcp\"\x98\xc4\x12\xa5\x11\x92\x19\xd2\xc4y\u0146\x84\xf0e*\x19\x00A\xf6\x87\b6\x12\x1aV\xfcf\xe7d\x84\xc4R\x94\xcfN)\xf6+\xea\x81\xe3!\x99\xcb)\x99\x83,\xf5\xa9>\x9d\xf0\xff\x89\u02fd#\x01+Hd\xfa\u0788\xbe,#-\xd55\xce\xf9Ak\xc1<I%*\xaeJs\x8b@\u2434j\xa5+\x05\x90\x17\x18\xe3*\xf4\x15B\xbb5K\xd2Y\xc0\x88\xad\xf9\xd5(\xca\xd4\xd4$'\x9f\xbcAO\x180\xefv\xbb\x02\xb0s\xe7\xce\U000ccc63@*\"Q)&p,^\xb4\x88s\xcf8\x85\x91\xa1\x06\xb3\v\xed\x1e\x05\xaa\xf6\xf0\xd6eK\x97\xa7\x87\xd7(\x9b\xa7\xa9\"\xaf\xc3D\x9e\xf5i\xbc\x1b\xa2\x8d\x18\x1d\x1e\xc5\xe0x|\xef~\xbe\xf9\u077b\xf8\xc7[\xbe\xc5#[vp\xe0\xe0a\x924%\xb2\x968\x8e\x18j6\x18\x1f\x1b\xf5\x96\xae\x87;\xa4iZi\xb3K\xe06\xc6\x10\xc7\xcd\x10,\x9bAeu8O\x05?2=\u00ddw\xdf\u03ddw\xdf\xcf\xdf\xde\xf4U.:\xefL~\xfeg\xae\xe1Y\x17\x9d\u0352\xa9q\xba\xdd.\xadv\xa72@\xd3\x1a\xad\xa0=\xc0\xf7T\xabg\xad\x1cn\xb9\xd9X\xfe;\\\xefZ|\xb8\xc5\u034fH\xf1\x1c\u007fHZr\xae\xf9\xbaA\x860d\x13N\x1d\xdd\u02f2\xc6\f\xdb\x16\x96\xb1ua\x05\xfb:\x93t]\xe4\xad\x12\xc41\xd8f\x98\xbeW\xabp]\xad\xccF\xa4\xa7\xfb)V\xc5T\x8aE,\xe9\xf1j/\x0fo\xa9\x03\x94\x94<r\xfe5\xc6Z\xef\xf2Wx\xa3keyE\xeb!\u0185[\xa4\xabk\x0fs\xbe\xda\x18L\xc3`\x87-Q\xd4 6C0\xaf\xa4\xb3)\u074e#\xcd\xc1?-\xb7@\v\x8aI\x15\x9b\x81\xcd\u007fm'\x14#\xd6[f8z\x17v\xbc\xa4/sy\xf5\xedc\xfe\u04ae\u00e5\xae\x88\a,\x14=\xb9|P\x14\xb1\xd2\xfb\xd0\xcb\xf7\xa6\u2a18'u\x19)m\xb7L\xe82\xb2BLZ\xbe\xf2\x92*$\x1a\xf2N\xb5\xa4XDzb#\xc3\xebe#\xe6f\xf6\xd1^8\x82\x89\xa2\xb0|\xe4_\x80F\xdc`~~~\xcb\xc4\xc4\xf8\x96\x13\x06\xcc7mzD\x00\xbe\xf9\xcdo\xd9\xfd\xfb\xf7\a\xfc\xd2\xe2buN9c\xe3z\u05af]E\x92\xf9\xb5\xe9B\xc1\xa2=&Z=\b\xad\x9800\x19\xec\xcb!O\x114\xaa?\xa1\xa4T\f\xa9\x13L\u0720\u0648\u0679{\x0f\xb7}\xeb{\xdc\xfc\x95[xh\xd3\x16\u069dnQ\x01\x8c\f\x0f\x95\xa9\u9744n2M\x969:\x9d\xa3o\x8by\xd3{C\x96e\xa8s\xc4Q\xeceVY\xea\x97I\"\x83s\xae\xf88|\xf8\b\xb7|\xe3vn\xf9\xc6\xed\\\xf1\xcc\vx\u00eb_\xca\u0557_\xc8\xe8\xe8(\xf3\xf3\v\xf5j\xb8\xd0E\x97+OR\xd3u\xe8\x93\x00r)4\xe9Z\xacs\xb9\xa3P:\x14F`\x90=-C\u0601\xfd\u0660\xac\x11\xa9\x87s/j\xcc3\x16w\xd80r\x90G\x17\x96\xb2ua9\xfb;\xe3\xb4]\x84\xc1a\xc5\r\xbcbt\xa0\x1d\x1a\x83\xe5\xb1\x15]\xb4\f\x98\xdf\xf6=\u007f-\xd7\xeds\xa9\x9d\xe4<o%\xc3\x14\xa9\x1f\xfc9\xa0\xd7\x1f\x8eT\x853\xa4\x81{\xb6&\xf0\xca&\xf0\xd9\x02\xd6\x1a\xe2\xe1\x06\xf1P\x8c\x1b\x03\x9d\x880\xb3)\xeeH\x02\xad<\x0e\xb1\x10\xb1P1%\xf5?\xbf\x9d\xa1\x89\x83\u0620C\x86\xacipVH(\xd5,I\xe6+\xef\xd4\xe5\xef|\xf8c%\xc4\xd2Ul\x03\xf2S8\v\xc3Q-+\xf4>O\xa1\x9e\x04\x8b\\)f\xa4tU\u0334\xbe\xdf\x14\x80\x06\xd3uu\xab\x82\x9a\xbfW-x\xd6\xdf\xf1b\x98\x9f\xdeO{a\x1a\x1b\r\x05\xae\xc7\u007fw\x14\u01d2en\xcb\xe5\u03feb\xdf\t\x01\xe6\xdf\xff\xfe\x1dr\xc9%?\x95\xaa\xaa\xb9\xf0\xc2\v7\x1c<x\xb0\x14\fT\u07a8SOZ\xcf\xf8\xa2\xc5>\x12\xac\xb2uXT/\xaa\x83\x82C\xca\x16_\x9e\xccx\xf4\x89\x83V\x16@\xdc!8\xb1\x8c\x8c\f\xd3\xee$|\xed\xd6\xdb\xf9\xcc\xdf\xfd=\xdf\xff\xc1\xfd$\x897M\xb3\x91er|\x9c\xd1\xd1Q\x8c1\xcc\xcc\xce07\xb7P\xab\xbc\xa3(\n`\xac\xfd\x8fQ\x95\xd6\xc2B\x81\b)e\xfaz\x1e\xde\x1b\xc51.P4Y\x96\x91\x05\xce\xfd\x9f\xff\xe5nn\xbf\xeb~\xae\xff\xf9\x97q\xfd\u03ff\x94\rkV2;7_.;h\x95\n\xd0\x01\xa0\xf5\xc4\x01]B\xc5\xdbk\x1cVMrr\xde\u06f2\xe0\xe7\xf5)\xd2:\xc7;l\a\xbe\xdbR.\xbdHOXF$\x19\x8b\x1bsLD-\u058e\x1c\xe2\u0445%l\x9d_\xc6\xfe\xee8\x9d,\xc6J\x16\xfc\xe7{\x0f\xb9:9\u0557\x88#u\xf8\x96\nG\x9bs\xad\xfdFg\xdaw=;J\xa7\xda\xe2\x00Q\xe7\x15-\xc1\x06 '\x9d\xfb_S)*L\x05Z\x99#s0\u0590bXh\x82\u0574\xb5\x96\xc8z\aFg\x14\x1d1hf\u04594x\xc7K\x90\xeeU\x0e(\xe3W\xf6Q\xa5\x91\x80\xa6\x8at\x95l!#1B\x1aA7\x12\x92HH\fE6kI\x93\xf8\u06575`\x9a\x86,Q\x9cG\xfa\x82K\x0f\xedT\xc0L\x87D\xa67\xcf|\xa0\xc0\xc9\xef\x06H\xed\x8c\x15\xa4\u0432;QL\xaa\x98\xc4\xd5_lWJ&\xa9\x1c^\x85r\u0265\xb4\x17\xa6I\x93\x84(\x1e\xae\xa5\x17\x18#\x9c~\xc6\xe9\x13\xcbW\xac\x1a>!\xc0\xfc\x96[\xben\x80l\u05ee\xc7N5F\xce\f\xa0&\u057d\xc2f#\xe6\xe4\rk\x19\x1b\x1fgvn\xc1'w\xd7\xe0z0<W\xdb\xfc\xa7c\xe0YW\x80\x18\xb2\x00\xe6\x19\u07bc\u007fll\x84\xc7\xf7\x1f\u14df\xf9\x02\x9f\xbb\xf9\xab\x1c:t\xb8,6\xace\xd1\xd4\x14\x93\x93\x93\x8c\x8e\x8e1<<D\xa7\xdb\xe5\xf0\xe12\x80$\xe7\xb3\xe38&\x8e\xbd^ya\xa1U\xff\xddZ\xa7U\xf2C@U\u0272\xccWL\x95\x96Y\x8a\r5G\x9a\xa6|\xf4\u007f}\x91{\x1e|\x84\xdf|\xcb\xeb\xb8\uc8b3i\xb5=\xad\xd3\xeb$\xa7\x03\xba\x97'Z5W\xfdbL\x8dX)\xdf\a\x13\u876c\xf0a\xa7F\x9a\xfd\xd83\xeds\x9a\xc2\xf4\xaf:*\x10\x19\u01d2\xc6,\x13Q\x8b\xd5C\x87x\xb4\xbd\x84-s+\xd8\u05d9 U\x0f\xfa\xd2\x03\xe8}\xae:\x15\"WzC\xa8k\x03\x90J}\x1f\x14\x16n\xc0\xe3-\\&M\x9dKpi\x86\xcb\xd2`o\xab\xf5\xfb\xa2j\xe1[8#zc)T\x19\x8a\f\xa6Fa{@\x8f\xac\xc5Z\x13V\xd5\x05\xd7\xce\u0226S4\xf5\x8a\x95\\\xa1\x97\x03\x9d\x06.]\x05\x92D=\x90K^LK\xa0\x80\xbc\n&\xb2\x824\xc4W\xeb\r\x13l\x90\xebY\x9fV\x80\xd8\x17H\x0e\xe7M\u01ea\x9d\x86S4\r\x87\xa4\x95\u06b6n\xcd\x17I*\aE\xe8\xfdL~\x9c\x06 W\x11\xc4\t&Q$\v\x94\x91\xcb\a\x9f\xc5\xf4\x13\u0514T\x95*\xc6Z\xba\xdd\x05Zs\x87k\x94\x98\x88\u0c8c\x91\x91a\\\x96\xdd\a\xec<!\xc0\xfc\u0421\xc3\x02p\xd3M7\x9ffmtN\xadA5\u07a8f\xe5\xf2\xa5lX\xb7\x8a\xc8\x1a2\x858\xac\xd1\xcb\xd1\x17\x1d\v3+\xa9\x00\xc7\xd33\xec\xf4K\x11\xa9\xda\xe0 \u8946\xa3\xe3cl\u06b6\x93?\xfd\xf3\xff\xc5?|\xf56@\xb1QL\x96\xfa\xaa|\xf5\xeaU<\xf7\xb9\xcf\xe5\xc2\v/`\xfd\xfa\xf5,Z\xb4\x88\x1d;\xb6s\xdbm\xdfd\xe7\xce]\xec\u0673\x9bm\u06f6\xd3n\xb7\xe9v\xbb~N\xb0x\t\x13\x13\x93\xec\u077b\xb7\xafe\xcei\xa827RI\x92\xa4\xf8\\><\xf5U\x96\xc1\x1a\xe3U@\xceq\xc7]\x0f\xf0\x1f\u007f\xfb\xfd\xfc\xb7_\xfdw\xbc\xec\x9a\xcb\x11\x91\xe0\xec&\xb5\x9c\xd2\xfc\u0412\xd2\x11\xfc\t\xe2d\x90\xe1\xf5\x01\\p\x1e\xacU\xe4n@\x16\u04cf\x05\xbb\xfb\xc1\xd1\xe4\x8b \xbdu\x81\a'\x14\"IY\u059ce\xaa\xb1\xc0\xaa\xe64\xdbZ\xcb\u063e\xb0\x94C\u0771`\n\x15\f\u07a4\xbf<\xac\x99\xf9\xf6\xc8l\xfb\x00=\xff_\xa75\x9fm*Fa9\xb5\x12\xca\xe8pP;\\\x9a\xa0YVKw\xea_?\u0329\x1a_\x8d\xa30\x12\x19\x0f\xacR\x89H\x93|\xaeaH\x9c\x90\x89\xd2\xed8\x92\x83\t\xc9l\x8a&Z0\x1e\x1aI}\xdb9\xf3+\xf0>K\xb3r\x00\xd9p\xad\xe6\xcfO\x15\x9b\n\xdaq\xb8\u06106=\xb0k$\xc5p\xba\x1cx\x12R8]\x91\xf8T\xbc\xbeN!\xad\x04k\xf4X\xe0\x1a\xa9\x843\x8a\v\u0665\xe1,\xc8\xc3,\xf2\f\x83T1\x9dJ\x9c\xa4\xcb3\x82\u02d7T(\xa9_\u007foY:\xadYZ\xf3Gz\xcc\u0494,K\x98\x9a\x9ad\u7b9d\xdf\x13\x91\xec\x97\xde\xf4K\x8d\x8f\xfe\xf9G\xbb?\xd1`>==m\x00\xb6m\xdbv\x92\xaa6\x8aN2\xac\xdef\x99c\xdd\xea\x15\xac\\\xb1\x8cV\xe2\xfc\xc6\x18Y\xc9\xd9\x1dc\xd8%\xc5e\xf9\xf4\x01y\xe6c\xa2\xfd\x96[\u042a\x8e\x8e\x8d\xf1\xe0#\xdb\xf9\xfd?\xb9\x81o}\xfb{\u0638A\x1c\x19\xda\v\xf3\x00\xbc\xe4%/\xe2\xcdo~3\x97\\r1\u02d6-#\x8a\x9a\xc5\xcf}\xc5+^\xc1\xcc\xcc,\xd3\xd3G\u0633\xe7q\xbe\xff\xfd\xefs\xe3\x8d\u007f\xc3\xfd\xf7?\xc0\x81\x03\a\x98\x9a\x9a\n\x11V\xd9\xf1\x1f\xa3j\x0fo\x1a\xb6\xd3\xc2.\xa1\x84e\x13\xa7\xca\xce=\xfb\xf8\xad\xf7~\x84\xf9V\xc2\xcf\xfd\xf4\xf3h\x1aC\xa7\xdd\xed\xc9\xc0|r\x15\xf9\xa0?f@\u022f\x14\n\x16\t\x16\xbdZR0OsY~\xac\x1f\xa7\x86\"N/_\xa1\xd7\xca\xd6 \x15*&2\x8e\x95C\xd3,j,\xb0f\xf80;\x16\x96\xb2}~)\xd3\xdd!\xef\xd0(\xae\x9c\xcb\u0220\xf7\xa6w\x96\u04eb\x89\xa9\x1eh=)>\xc5PS\xfa\xaa~M3\x0f\xe6\xce\xd5N$? 5\x18\xebm\xe0\xc8\x14\xe7\xb20T\xf4\x1cyd\xbceK\x8e\xe2\x12~h\x06$\x99\xd2\xe9d$\x89\"\xd3)2\x9b\xf9 \xe3\xf0d\xa4\xfaV\x85\x83\xcf8*1r\x94\xe1\x11\x198[\x1e\x90\xb9\x85\xae(\x98,\xc3v\x85\xac\xad\xb8\x86\xe0\x1a\x824\fD\x94\x9e\x97\xd6?\x17\xe9:4\xed9\xf0\x83O\xbb\x982tBzl0\xf2m\u06b2\xf8\x97\x9aK\xa5(\xd0\r*\x16\xf5\U000c0a85\xb6jM\xf2\x12\xdec\x1f\x1e\xd2\xed\xcc\xd1i\xcf\xd6<\xa4P%MR\x9d\x9a\x9c\x94s\xcf9w\xfc\xe6\x9b\xfe\x1eu\xee'^\xd1\x12}\xe4#\u007f\xde\x05\x98\x9b\x9b=\xe9\xb1\xc7\x1e\x030Z\x88\xf4\xfd\xab\xbdr\xc52\x16OM\x92%\t\xb1\xb8\xa2\xea\xabUY\x03\xd6\x02\r\xfa#i\xa6\xfb\x95\x19\xf8\x19L\xd3\x00\x00 \x00IDAT\x86.Q\xe1i\x8d*c\xa3#l\u06f5\x8f\xf7}\xe8/\xf9\u05b7\xee`dr\x92\xd8Z\xe6g<}r\xfd\xf5\xd7\xf1\xeew\xbf\x8bU\xab\xd6\xe2\x95;-\x16\x16fQ\xf5C\xa6\xc9\xc9I\x16-Z\x02\b\xe7\x9d\a\xd7^{-\xaf~\xf5\xab\xf9\xfd\xdf\xff\x03>\xf6\xb1\x8fs\xf0\xe0\xc1\x10\x97\xd7\xf3\xec\x02\xb7\u9723:,\x1e\x04\xf0\xf9A \x81\xb7\x8f\xc4\xe02\xc7\xec\xdc\x1c\xbf\xfb\xfe\x8f142\u032b^\xfa<27G\x92$E\xcdl\xa4\xf4\x16\u0441U\xfb\x13H\x19\xa2\x8eo}1^\xbd\a\xad<}`=H\xe1\"\xfd\xa7M\xb8\u0524\xcc*\xed1m\xf3]\xa2_\xa6id\tk\x86\x0f\xb1\xb41\xc3\xda\xe1\x83l\x9e]\xc1\xf6\x85e\xb4\xb3\x18+>]\xaag&YqN-\x97k*\xa3\x9e\u0497\xa7w6\xa1u\x85Q\x9e\x1eo\x83\t\xb8:\x87K\x124\xcd\x02\xc0jA\xc5\xd9(\x06c\xc8B\x97G\x14\xa3b\x8bMb[XDT\x06\xf9\xea]\xfe:\x0e\xda*d\x89bgS\xe2\x85\f\x93\xe5+\xed\u07a7\xa4\xf0\x8fq\x83\tN5R\x02b\xbenoKUHMq\xe2 \xea8\xe8\xf8\xe1\xa16\x8c\xff\x88\r.\n\xbe5\xc6K'\x1d\x9e\xc2\xd1\xfc\xec2~1\xce\xd4y\x16\xaa\xf3J\x18\x14\x82\x9eK8\xfd\u01a7\xe9:$+\xccX*\xd66=I\x00A\u032e\xea\xedE:\xadiZs\x870\xc6R\xb5hTU\x19\x1d\x1de\u035a\xd5\x1d\x80\x95+W\x9c\x18j\x16U]\xf5\u05b7\xbe\xf9\x92\xb9\xb9\u067e[\xd1Z\xc3\xdaU\xcbX41J\xbb\xd3%\x92\xca\xd0(\xf8G\f\uea5f\x1e \xa7\x02\xe4\xa5\u046e\xe7\xa1\x1b\x8d\x06\xfb\x8e\xcc\xf1\xc1\x8f}\x86[o\xfdg\u0196.crb\x82\xf9\xc3\aH\u04c4\x97\xbe\xf4%\xfc\xe1\x1f\xfe\x01\x8b\x16-\xa5\xdbm\x15\x83JkK\xb3\v?\x1c-S\xa5\x9a\xcd&\xa7\x9dv\x06\x1f\xfe\xf0\x9f111\xce\xfb\xde\xf7\x81\x822\xa9\xfei4\x1aDQD\xab\xd5\xea\x931V\x01_{\xac9\x9dSo\x05`=\xed277\xcf\xff\xfc\xe0'X\xb3z%\x97\x9ew\x06N\xe7\u0086\xa2T\xb8B\x18\xb0\n\xd3'0<^\xf73H\xc9.O\xcb \xfa\x89\x02\xfaQ\u010bR\x16\x04\xd2[\x1d\x88\x14\x99\xb0b\xbc6\xc7:\x88m\xc6\xc8\xf0\x01V4fX3t\x98\x87\xe7V\x05>\xdd\u0516\x8e\xaa\xd4Sm^_qZ\xef\x9bs\xd7\u01a4\u055f\xe0\x15 \x9a{\x91Wl\x1c\xb4\xc2\a\x18kI\x93\x0e{\x1fy\x80}\x9b\u007f\x88s\x19\x93kOa\xc5\xc6s\x18\x19\x9f \xa4^\xf8\x85\x9c\u04359\x85\xaeS\xda\x0e\x92\xc8\u03c0\x1a\v\x19\xf1\xbc\arq\x81O\xce\xc1\xbc7\xb0\x9b\xba\u04e1\x14\xe9\x12\xfe\xf5\x13\xa5\xf4twR\xd0\x1cyu\ud36c\u00b5\xd1q\xa88L\f\xb6ap\x91\x906<\xbfN\xb8n\xb5\xdaI9\x87\xba\x10\xfa1\xd0)I\v\x17H[\x8dc\f\xef\xbb\xc9\x14\x9b\x849E\x90$\xaa\xcb\xfd\u07a9\xa5~ke\xbadl\xc4\xcc\xe1\xbd\xcc\x1c\u068d1\xd6\xd3>^J\ud187\x87\xcc\xec\xec\u0716\xd1\xd1\xd1;\x00Z\xad\xb6\xfbI\as\x03\xf0\xf8\xe3;\xd3M\x9b\x1eiW;\x11\r\x14\xc1\xe8\xc80\xabW,\xa3\xd9h\xe0\x82\v]\x8d\xa9\xcb#L\xfa\x86w<M@^\xbf\xd7L\x80uk\x04L\xccM_\xf9&7\u007f\xf9\x16\xec\xf0(K\x97\xad\xc0%\x1d\x8e\x1c>\xcc\u06b5k\xf8\xad\xdf\xfaM\x16-ZJ\xbb=\u007f\xcc\xea\xb9\xfa\xa7\xd3\xe9\xd0\xed\xb6\x88\xa2&\xbf\xfd\xdbo\xe79\u03f9r`U\xdel6\xb1\xd6\x1e\xf5\xe7\xd6\x16\x8dz*\xf54s \x86F\xec=#v\xed\xde\xcbG>\xf9\xb7\x1c<2K\xa39\x84\x18\x9f\xb7\xeaz\"\xb7\x8e?\x16>\xfek\xd8o>\xf6\xe4\x17\xfc\x95\xbe\x9c\x80'\xf0(\xe8s\xd7\u0523\xf0\xe8j\x14\xb5\x12\xbcG@\xabv\u06a6\x944\x1aq\x8cEm\xce\x18\xdf\u00d5K\x1e\xe6\u00a9G\x99j,\x90\xa9\x1f\x8eW\x96\"\xfbsV\vu\x86\x14\u04b9\xa3\x0f\xf4\xa5\xc2g{\x83\xa8\x99N\xca|\xab\xeb;)\x97\x15\u065e\xbe\xdd7l\xff\x97o\xf2/7~\x84\a\xff\xf7\x17x\xf8\x1b\xff\xc0\x03_\xb9\x91G\xbf\xff\r\xd2n\v\t\u05cd\xaa\x97\b\xb6Re\xbe\xebXH\x95D\f\xd8\xff\x8f\xbd\xf7\x8e\x92\xe38\xefE\u007f_Uw\xcf\xcc\xeeb\x01,r \x88@\x80A\xccA\x04\xb3$\x9a\x92\xa8@E\u0496D\u0274l_Y\xbe>\x96l_\xf9\xfa\x9e{\xed'\xd9\xef\x1d\x9fg\xeb:\xe9]\xcb\u05b3h_\xcbV\"MR$%J\fb\x00s\x06!0\x00 r\\\x84\xcd\x13\xba\xab\xea{\u007fTuw\xf5L\xcf\xee\"\xf0<\x1f\xc1s\u0392\x8b\xdd\xd9\xd9\xd9\x0e_}\xf5\xfb~A\"l2\u00ba\xca0p2\x16\x13\x87F\u0198!?(\xdd/\xe2\x9c\xd1GrV\x88+\xfa\xa4Sow\xce$\xfa\x94a\xe19\xffR(\x83\xa0i\x10\x8d\x1bD\xe3\x1a\xd5\x11\x85\xeap\x82\xa8i\xdd\f)\xcb\xe8K\xb38\xb9\x80e\x15\xdc=3\x8b\\\u4ed2\x141ag\xaa\xe5\x06\x9e\"\xc3\u033d\xdd\x15\xf9\xc3\xcd\xd4)S\xc0h\x85\x91\u00fb\xd1l\x8c\x822Y\xafe\x17\x85a\x809s\x06\x0e}\xe63\xb7\x1c\x06\x80\x8b.\xba\x90O\x8ab\xfe\xdcs\xcf\u03cb\xa2\xca\x1a\u02ea\xc8\x1f\xda\x18\xcc\ua7c1\xb9s\x06,\x8f\x1f\xc2\xcf\xf1*/\u0669\u007f\xc2\t\xf3\x18\xf1\xec\xb1R~\xb0\u327f\xf6\xc6\x16|\xef\xce\x1f\x81c\x85@\x12\xf6m\u07c2}\xbbl\x00\xf5\xfb\xdf\xff~\\y\xe55\xd0:\x9eV\x11\xef\xbc\xd1\x15f\xce\x1c\xc0M7\xddX\xfa\x9c$I\xd0l6\v\xf8x{1\xef\xf6\xef\x94\xd9\x02BV\u041f|\xe6E\xdc\xf7\xe0:DQ\x05\u0489\x1f\f\x93\a_\xf11\x1f\xd3\xc9\x13\x9b\xe8\x04\xcc2\xa6ge\u0325\xef\x87:\xb9\xe1i\x05\x16%\xbe\xda\u0085P\vd\x1e\xd8\x01\x19\u0329\x8c\xe3\xfc\x99\xdbq\xd5\xc0\x1bX\u077b\x1f\xa1\u0408Y\xc2\x18j\xdb\xc1P\xa9\xc8%\xfb\xbc\x8d\u0398\xef2s\x06\x86\x01c\"N0<\xd1\xc4\xc8D\x82zK\xbb\u0754\x86\x8c*8\xbc\xebMl\xf8\xc9\xed\u063f\xf9g\x98\x18>\x84\xfa\xd0A\x1c\u0679\x05\u06de}\x18\xc3{v@\x86\u0591>a\xa0\xa9\x18-\x05(&\x18!@2@\xd4\x12\x88\\G\x0e\xb8n<\xfd<-\xca\u0736*\xa6\x05\x10\x19\xa4\xecX!\xa978 \x94}\x9dla \xce\x16\u0334\xcdN\x03\x93\xd3\xd7\x13\xca@$\f\x910d\u04e02\xa6Q\x1b\u05c8\x9a\x06B\xe76\xb4E_k\xdbY\u06cf|x)|\xc8%\xe5\xdbk\x06Z\xc6\xce\x03\x98<\x16\v2\x87\xd2\xf6Z@ H\x19\xa211\x84\xc3\xfb\xb7\x80\u074e;}\x8aVZ\x0f\f\f`\xf6\xec\xd9\u06c8h\x1b\x00\\v\xd9e'G1\x1f\x1b\x1b[5s\xe6\x8cU)\x1f\xdb/j\xfd\xfd}\x98=\xab\xbf\xe0Q\xc2\xecd\xf3\xae{4De\xb5\xf7\x84\r\xd0l!\u03e1\x96\xb0RE\xbd\x11\xe3\xbe\a\x1f\u014em\xdbA\x81@k|\x14\xadF\xbd\xd0a7\x1a\xe3\x19m\xf0h\x1f)\u03bdl\xd9)\b\x02Y\xda\xc1+\xa5&\xc5\xca\xcb~o\xfau\xcbC\xb7\x17a\x14\x06\x98\xa87p\u03cf\x1f\xc1\xe0\xfe\x03\xe8\x89B\x80\x04\x98\x84c\xb3\xe4v\xab'\x92i\u00a5{\x9f\xe9/\x0e\x1eWa\xda?\u05feK\x98\xca\u317a\rN]{g\xa5\xf8\xb6\xfb\x8bHci\xed\b.\x1b\u0602\xb7\xcf\u078a\x05\xd5\x11(\x12HXf\x96\x00\u0183\xac\x8a\xd3\x03W\xe8\x8bI%\x056E\xaa\xa2\xd4l\xa0T\x82VKa\xbc\xa50\xd4\xd0\x18j\x1a4b\r\xc8\x10{__\x8f\xfdo\xac\xb7\xf1t\x89\x85b\x92f\x1dC{\xb6a\xe2\xc8 \x88$\x12m\u0408\x8d\xa5(\x12A\x93\xe5\xe5\u0216@Xgk\xdce\\A\u0576\xbb\xee(\xe2\xd3\xd9\xca\"g\x87\b\x9d\x17u\x19[h\x03\x9c{\xbe\xf8\x8b\x85\xff\xb3\xc20\xa4\v\xe3 \xc3\bbFe\u00a0:n\x10\xb5\xac\xc8'\u0760\xa7\xb9\xab\xac\xed\u0417\xb5\xb1\x9fgktq&!\x13\x03R\xce\xee\xd6\x13\\\x11\xfc\u0646g\x94\xee\x16W\x19D\x98\x18=\x8cC{7e\xc3\xcf\xf4\xacj\xa3\xe5\xc0\xc0l\u031f?o=\x00\xfc\xf1\x1f\u007fY._\xbe\xf2\xe4(\xe6\x1b7\xbe\xaa\x06\a\a\xb3A\x9f\xaf`\xeb\xed\xe9A__/\x8cq\xd1j\x1e\xf9\xdf\x0e\xcf\xe9-\xf3~7Y^g\xfe;\x04Y\\{\xeb\xae}\xf8\xe9c\xcf\x00\xac\xc1\xaa\x93q\xf4\xd0C?\xc5#\x8f<\n\xa2\x00\xc7P\xcb\x11\x046\xb7c\u02d67a\f\xa3R\xa9\xa0\xaf\xaf\xaf\xd0]\x9b.\xf1&e]y\xe9\x82BdS\xe3I \f\x02\xbc\xbey+\xd6=\xf5<z\xab\xa1g\x1bzb|\x14\xa9t\xad=\xf6\x84\xa2b\x8eT{\xb6ig\xc7\xde\x1e\xd0L\x1d{\x83\xee\u007fkY\u007f\xc0\xe4D/d\x1d\xf9\x02h\baS\xafz\x83\x16\xce\xea\u06c3+\xe7l\xc2Y\xfd{\x10\x89\x04M-\xa1\x8c\xf0\xc6\xc0m\xbf\x81\x8a\xdb\xf9\u00bb\xa7\x9c7h\xc06\x1cZ[:\"\xd8F)N\xc4\n\xe3J`xx\x04\x83\xdb\xde@\xab>nAA\xa3\xecN,\x8eaT\x02\b\x89\xa6f4b\x03\xc5\x04&\x81\x04\x84\x04\x04\xd1\x12\b\x1b\xce\x02\u0597\x9cr\x8emw\xc2VS\\%\u073ek\xb6\xf0\x8c\x15\xe80d\xcbv\xde\xc4m\x8b\x19\x173VH\x1b\xdb\xd9\x1b\xc7\xccd \x88\r\xaa\xe3\x1a\xb5\t\x8d\xb0e t\t#\x93m\xf7M\x86\xddB\x98\x8f\xf2Y3\xa8e\xa1#\x06\xdbP\x0e\xb7\x80\xb1\xe1\u04acS\"\u01dc\x010zd\x0fF\x8f\xec\xc9\xee7v\xbb\x820\bDT\xa9\x1e\xbel\xed\xda\r\x00\xd0\xdb\xdb\xfbs\x8f\x97g\xc5|\u03de\xbd\x18\x1b\x1b/\x14\xa1\xf4D\xf6\u052a\xe8\xed\xadA\x9b\xd4ZV\x14\xd8%\x9aDi\xfft\"\x18,9\xb4\xe2\xba$X\xdb\xd9$Qx\xe9\x95\u05f0w\u07c1\u049f\r\xc3\x10\xbbv\xed\xc2]w\xfd\x00Z\u01e8\xd5z;\x86\x93\xdd!\x16\xb8N\\\xe2\xe0\xc1}\xb8\xed\xb6\xdba\x8c\xc1\x9c9\x03\xe8\xef\xef/\fO\xfdb]\xc6x\x99\xb2\u023b\xf7$\xa4D%\n14:\x81'_\u0608\x91\xb1\t\bY\xec\x96\u028b\u0431\xc3-8Nw\xc6\xe9\xa0\xf7<\t\xd43\x95?\u03d4E=\xc3\u05d1:2\x81\x04C\n'\x1b\x17\x8c\xf9\x95\x11\\2k+.\x1d\u0602\xf9\x95Qh\x16HXt.\xeel\xfd\xc8\xd9\xd8\x0f\x93\x89\u007f\x00_\xe3\xcf00:\x86\xd61\xb4\u0459\xe5-\x01\x10l\x80\xb0\x8a\xc1]\xdb1\xb8e\xa3\xf5\xc6\xf7\xa6\n\xba\xd5@\xcf\xdc\xc5\b\u7702\xb1F\xcb\xc2* (\"(\x10\x82\x16\xa1\xda$\x04\x9ar\u1303X2\x8eu\xdb\x11\xe2\xc9\x0e&u\xc20\xed\xc5\xd1\x0e\x1e\rdl R\U000bbdc0\x907t\x04\xac\xa7\xb8L\\QO\xb1k\x06\u0096AuB\xa32\xae![^\xb4\x90scd\xc30:\xb7\x83Na\u007f\xa9\fD\xa2s\xe8\xc8\x15\xfd\xfc=\xd87[8\x1fD\x10A\x00\x954ph\xef\x1b\x88\x9b\x13\x10Bf]\xbf1\x86{{{10{`\xd7G>\xfa\xf1\x97\x01\xe0\xf2\xcb/\xc3IS\u0337n\xdd\xdaj4\x1a\xa5\u017c\xb7\xa7\x8a\x9ej\xc5%\xebp\xe6H\u01c0\x1b4\x15\x15\x1f'\x14\x06\xf0H\r\xa9<=\f\x024\x1b\r\xbc\xfc\xcaF$q\xdc\x15\"\x91R\xe2\xb6\xdbn\xc3_\xfe\xe5_\xc1\x98\x04\x95J\x0f\xaa\u055eI\xf1s!\x04\xaa\xd5\x1eHY\xc1\xd0\xd0!\xfc\xe1\x1f\xfe\x1fx\xfa\xe9\xa7\xd1\xd3\xd3\x03\"\x01{\x8cDa\xfb\x97\x0e@}X\xa5\xfd\xff\xdd\x06\xa2\xa9\xe4\xdf\xf2\xcf\xed\"\xb1y\xfbn\xbc\xb1c/(\b\xf3F\x8c\x8e\xbf\xc0v\xde\xcb\xdcm\xea1\xed\xd7,-\xc0\xdc\xd9G\xfbW\aO\xf1zG\x15\x9dG\x80\x91\xf6#\xa5\u03f1d\x904\x10\xc26cU\x19cM\xdf~\\=g\x13\xce\xe8\u06c7\x904\x12\x0e\xb2\xdcX6\nF\u06ee\xd9h\x05\xa3\x95\x15\x00\x19c\x8b\x88\xfb}\xc6\x18(\x1dC\xe9&4+K746\xf4\x02N\xcc\"%\xe1\xc8\xf6\xd71\xb2{+d\x18Z\xbb\xc0\xecZ0\x98\xb9\xe2LD\x8b\x96#Nb\xb0\x14PD\xd0L\b\x9a@\xa5\x01H\x96 \a\xed\xb0+n0%\xf88\nU\u0583\xe1:q+\xa2I\x86\x1c).\xaes\b\x86<\xdf\xf0\x8c^$rA\x15\xa50\x8d\xe79N\xb0\x01\xcbA]\xa3:\xaa\x10\x8d\x1b\x88\x98\x8bR\xfe\x14z\xa1|6!2n9\x1cf\xee`Hmrr\xb9\x9f[\xe7\x86\x01RFhN\x8c`\u07f6\x97aX\x81\x84\u022c\x01\x94R4o\xfe<\xacX\xb1|\x1b\x11\xed\x01\x80\xb5k\xaf8y\\\x13W\xacX1\xe7\xe0\xc1\x83mrg\x06\x91@__\x1f\xaa\xd5j\xb6\xaa\x8a<\xab+\x0fa\xed0\x14:1\x83O\xe6\xdc\u0396\x1d\x8fIJ\x81\xb1\xf1:\xb6l\xdd1i\a\xcc\xcc\x18\x1a\x1a\xc6\xff\xf8\x1f\u007f\x84\xad[\xb7\xe2s\x9f\xfb\x1c\xce>\xfblDQ\u037b\x9a\xd3\xddW\xdem\x8f\x8d\r\xe3\xb9\xe7\x9e\xc3_\xfc\xc5_\xe3\xbe\xfb~\x840\f\xd1\xd7\u05cbF\xa3\x81\x89\x89\t\x18\xa3\n\xf8w\xd9\xe7\xdd\xdeS;,\xc3\xccPJ\xd9E\xa4RA\x14\x86\u0633\xff\x10\xde\xdcy\x00\xabN[\x05\xc3\rOP\xc7G\r\x8a\xe4\xae+\xe4\x05w\x94\xe1\xde\xc7\xd7\xed\x17zF\xe2i\xf5\xed]\xbd<\xa6\xf9\xbe\n\u040c\u021d\x03\xb3\x81.3\xa4+J\x02\x1a\v\xab\u00d8\x1140\xa72\x86\xd7\u01d6\xe0H\\\x03\x99\x040IfA\x9b\xdf\x00\x1e\x01\x9d\xac\xb3\x9f6\t\x14+(\xd2Pnn\xc4Y\aK\x10Q\x15c\a\xf7a\xf7\xf3\x8f i\xd4!+\x95\xac\x90\xeb$F\xcf\xc0B\xcc[y>$\"$\xba\t\x04V{\x1f4\x19QL\b -\xbd.-\xc0&\x87U\xc8cw\xb4w[\xbe\x10\xa7c\x89%\x14B6:\x96N\xff\xe9\xda\u044c\xdb\x17\x03\xedL\xca\xd9[\xfc\x8d\xa5\x87\x8alwdM\xceSk\x01\x91\x18\x04\x82\x90T\b*\xb2\xb35\x18\v\xb7\xb0K\u0720XC45\xa0\r\xc8\x18\x90vN\x93\x86Q\xd0\xf702\x85)9!\x163chp+\x0e\xee}\x1di\f\x94\x15\x9fYk\x8d\xfe\x19\xfd\xb8\u448b_\xec\xb6#\xfe\xb9-\xe6\xcc\\\xf9\xda\xd7\xfe\xfa\xba\xc7\x1f\u007f\xc2Q\xab\xd2H)\x06I\x81J\xb5\n\x11F.\u02638\x1cI\xf3\x1b\xa9M\xa4q\x82Jyf\u07aa!m\a\xe3\x9c\xea\xc7\xc6\xc70661)\x1e-\xa5t\x8e\x88-|\xfd\xeb\u007f\x8f\a\x1ex\x107\xde\xf8q\\~\xf9\x15X\xbat\tf\xce\xecG\xa5R\x05\xb3\xc1\xc4D\x1d\x87\x0e\x1d\u009bo\xbe\x89\a\x1f|\b?\xfe\xf1O088\x88(\x8aP\xadV\xd1j\xb5\xd0t\x1e*\xedB\xb2\xf6\xae<\xc3\xef\xa6\t\u0527\x05\xdd0#\f\x03\x8c\x8cO\xe0\xf0\u0430\x1d\xdcN\xa3\x1b\x9e\xea\x18v\xf1\x13<a\x90J'\xc2\xec'^r\x9bUr'R\u007fBo\xb3BZ\xbd\xfdL8\xf1\x1b3\xa1/h\xe1\x9c\xfe=\x98\x1d\xd6\xf1\xfc\xa1\x85\xd83\xd1\x03\x06#\xa0,t\xceI\xdd\x18\f\r2\x02\x06\n\xac\x15\f)h\x97\xf8c\xfc\u25aa7E\x80\xe1\x9d[px\xf3+\xa0\xc0\xcdi\x8c\xf3\xf3V1\x06N=\x13\v\x96\x9f\x0f\f\xd7!\xc9\xca\xd9E\x02DM@\x92\x04\x85A\xa1\x90\x921\x85\x98\xc3\x0e,\xd0;\xe8L%K \xe73\x00\xe2\u026c\x89\x19\x1d\xb2X\u07fa?\xf5L\xf7\x95\xc9^\x88\xb3I\x8f\x81\x82\x15\x10\x05\x96\xc6\x19\x18\x03\xa1\x00\x11\v$5\x01\x1d\u0602n\x17\a\x03\xd9L \x12\x97\u03ea\x8d+\xea\uc0ba\xd9\xdbY\x14m\x1a\x88$t\x12c\xdf\xf6\xf5h\xd6G\xb2\x9dA\x9a\xe9\xda\xd7\u05cbY\xb3f\xed\xfb\xe0\a\xde\u007f/N\xb2G\x00 \xaa\xd7\xeb\xb3\xe28.x\x8d\xa4\x85\xc6xqN\xec\x05\xf5\xb6[A\xe5\xcf8\x91\x18\x10g^\"v[l\xb1\xb3V+\xcf\xdb,+\xe8\u030c(\n\x11\x86\x01\x9a\xcd\x16\x8c1\u0632\xe5M\xfc\xe9\x9f\xfe\u07e8\xd5jX\xbe\xfcT,]\xba\x14\xfd\xfd\xfd\xd0Z\xe1\u0211!\xec\u0631\x03{\xf7\xeeE\x92(H)2\x1e\xb9R\t\x94\u0496O\u0705\x9d2\u067f\xa7\u04f9k\xadQ\xaf\xd7\x11\x86\x12:\xd1h6[^D\xd6\xe4\u066aS\xb6\xad\xa5\xfd/\x9f\x90\xba\xe9\xef\xfa\xd3\x10\xe7t\xaa\"\xbc\xebc:\xc1\xd5\xc7\xea\"@S\rh\t\x10\x82All\xb2\x0f1\x16\x84\x838\xafo\x04\x15^\x80\x1d\xf5\xb9HL\x88@\x98\xec/I\x17\"m\x94\xc5\xd1YC\a\x8e\xcd\"l\xfa\x0fs\u0389\x11A\x05\xad\x891\x1cX\xff$\x92\x89\x11\xeb\xab\xed\\7Y)\x84\xb5>\xcc_}1j}s\xa1\x1a\x13\b \x00e Y@\xb2\r\xba \x9b\x9e\xec\xe2x\x8c\xc5\xcd\x1d\xae\xdd\xee[\xe3S\x83\xbb\xda\fS\x11\xf2/\x1ea*\xc1\\\xa8}e\xc8\xe1\x96IN\x90\xe0\xb4\xe3v\x87N[\xa6\x11\t\x824\fj\x1aH\u0148k\x02\xa6F\xd0\x06\x10-\x05\xd9T@\xcat\xf1\x84P\xc4%\xa3t\x8f\xf9(\x84D\xdc\x18\xc1\xbe\x1d\xaf@%-\x9b\u0104\xdc\\k\u05acYX\xb6\uc517\x06\xe6\xcc\u007f\x19\x00n\xbd\xf5\x1f\xf0\xd9\xcf\xfe\xdaIS\xcc\xcd\xf8\xf8\x84\xb2\xea\xc8\xf6\xb9\x90\x8d\x87#O\xfak\x9d\xce\u0716\x06o5\x14e\u007fG\x00\xbb\xb5\xd5,\xc0\x9cg1NVP\x93D\u0658\xb8\xcc\xd2\xd6~4\x1a\r\xbc\xf6\xda\xebx\xed\xb5\xd7\xdb:y\x01!l*Q\xfa\xbbS\v\xdb\xd4\x1dq\xb2\x9d\xc0d8<\x11ux\xbb\xb4o\xfd\xb4\xd6v\xf0Jde\xdf\xe9\xce\xe7X\xecR\u0607?\xca\xfb\xe7\x13a\xc1R\x9c\xb1\xe5\x11}HM\x99\xb2!+OVk\xba\x0e@\x8f\xb7\xb8\x17\x1aV\x01\x10\x1b\u8111$\x06\xb3\xc2\x18\xe7\xf6\xc7\xe8\x93-l\xae/\u0138\xa9A\xa6>Bl\xb1p\xc3\x16;gg\xfc\xc5\x04ha\xa09\x8b\x9b\xb2\xc7X\x06\x18\u06ff\x03\a6>\xe3\x1c\xfd,<c\xbb\xf2\x04\xb3\x96\xac\xc1\x92\xb3\xaf\x02\u01c9\u015c\x99!c\x03\n\xa5\xfd \x99moEb\x99&\xe4\x0f\x01y\x92\xa3B\xc7rd\xb8m\f\xde6E\xed\xf6\xfb&3cb\x82\x1f\xf0$`G\x06`@*F\xb5\xae\xa1\x8c@R%PC\x01-\x9dY\x06\x93i_9\x8ao\xbb\xfd\x8a\x1d;\xbc\a#\x87w\x81\xd9@P\x98\xef@\xa4@\xadV\xc3\xc5\x17_\xfc\xec7\xbfy+\x00`\u0252%'Ug.*\x95(\xb0l\x8c\xce!]\xa2\x8dU\u04f9[W;\x0f\xb5\xc0\xf3\xc76\u07ad}\"}\x9aRC\xa8\u020d?\x15B\x18\b\xd4j=\b\u00e0\xf4\nK\xa3\xbe\xe28F\x92(\x10Y\u0225\xb7\xb7\x17J)\xe7\x88\xc8\x1dC\u0254\x8db\xa1\x14\u04f1K)[8\u02be\xef\u007f\xcdz\x9c\aY\xb1n\x1f\x86\xfa\x18\xba\x94\x12\x86\x81J\xb5\x82\x9e\xbe^\xebvF\xf9\xed6\xd5}[\x0e]\xb0\xe7\xfbGm\xde\x18|\xc2\nz\xcem*f\x98\x9a4U\xa6\v^\xde\xfe\xbe\u0465_\xc4\x14\x85\u007f\x1a[<\xc0\x00\x891\x88\x95UkJ\x10ze\x13k\xfa\xf6\xa1\x16\xc4\xd88\xb1\x14\u00ea\u05f9\xbc\x1b\xb7\vt\x1f\xc2\u01b2\x19\x10\x8ct\x16e\xce}\x90\x82\x00\xac\x15\x0e\xbd\xfa\x1c\x1a\x87\x0f8\x9c\xdaR\x16M\x92@\xd6z\xb0\xe4\x82w\xa0\u007f\xee2p+\x86\u0400d\x82 \xe1\x02 \x02\x90\xb4!\x9dB\xdbB\x9e\x8es\njN\x9ad\xeaL\xd3\\\xe1\v\u01efK\x11\xf7\xfcMJ\xa7\xa7\x05\x03\xb2\xe2l\x92)wo$r\x89G\xc6v\xed\xa4\x81\xb0i Z\x06\xa6\xe9\x8c\xc9REQA9\x9a\xbaVr\xdbN\xc45\x92\xcc8\xb8\xefu\v\xb1Pj`g\xdb\u02be\xde^\u031f?\u007f\xec\x82\v.\xfc\xe7\xf4\x1d\xbf\xe7=\u05df4\xc5\\\x000}}}:-\x8e\xed\xc5|\xbc\xde@3V\xae[\xcco=M\x02\x86D1_\xf2-0O%\x17\xa3\x10\xc2 d\x9b\x93\xd2\xdb\u05cb\x81Y3;\xe0\xc3\x0e8\x91M\xc6\x18\x01r\xee\xb8\xefh\xa8\x94\xca>\xfc\xdc\u03f4\x93\x9fL\xe19\x9d\x0e=}\x1d\xff\xb8\x96\rd\x02)\xa1\x12\x85\x19\xfd\xfd\x98;g\x00P\x89\x8b\xdc\xe0\xa3,\xac\xdd\x06\xa1\xd3h\xb0\x8e\x037\xb7;(ktea\x96\xdc-3\u035d\xe4B<F9\x8d\xb1\xb3\x80S)\xc5q\xda\xfc{\xd7\xd9%\x86\xd1T\x06\xb1\xdbm\xba8\x13TD\x8c\xe5=\aq\xc1\xcc\xedXP\x19\x82fF\x02@\x933\x81%\x06\xa4\xf3\x10\x97\x04\xd3\x16\xa2,\xc3\b\xcd\xe1\xc38\xb0\xe1)\xe8\xa4i\xbblv\xbbZ\x15\xa3o\xd1r,\xba\xf8\x9dV\xc3n\x18\xc2\x10\x88\x05\b\x02\x02\x815\x88r\xa6\xdfT\x12T\x92\xc1\xf2\x93\x14g \xa7\xf0r74\x8d;\xa1\x19*\xdb\xc1\xa4\x04s\xf2\u03d4?\x11\xednKIi\u051bv\xac\x17{\xe8\x9c\xf2\u050a\x8f\x82F\x02\xd9H\x80\x96\x8d]$\xce\x191y\x00F\xa7\xae\x98\x9d\xe3\xa8J\x1a\u063f}=\xe2\x86sJL\xf9\xff\x86\xd1\xd3\u04c3s\xce9gd\xed\u06b5\xdb\x00\xe0\xa7\x0f\xff\xf4\xa4\xc2\xcc\x05\x11M\x1c92\xf4Hoo_\xd61\n\x91\x9f\xacCC\xa38<\xdap\x13c\x91\x8d&\x99\x84\x93\xe4\x8a\xc2BM'\xb8X\xa4\x97\x9f-\xe8\x1aP\tzk5\x9cq\xfai\x90R\xba\x01\xc9\u4152\x99Q\xaf\xd7Q\xaf\xd7]\x81FV\xa8\xfd\xe7\xb4\x17\u0763-\xe4e_O\x17\x8a\xc9\x06\xa4B\b\x90\x10PJa\u1885X\xbel1L\x12\x17\xb2\x0e\xdbi}\u0736\x1b)DV\xb6a\xaa\xa5\x93\f>\x91\xe7\xa73\x9b\x91\xdb \x16\xf6\xfe\xc5m\x03\xb72\x04\x17m\xcf/K\x02\xa2)\x8azZ\x9bbePO\x14Z\xda@\xbb\xf0cCl\x8d\xbb\x84A$\x13\x9cR=\x88\v\xfa\xb7\u151eA\x18\xa1m\xd6&\x19\x1b\xf8\xe0B\x1f\x8c\x042\u0751\vxf0\xf6\xbd\xf8(\x86\xb7\xbf\xe6-\x1e\f\xa3\x15\x82J\x0f\x16_p\rj\xa7,G\x12\xa5\xbclw\x8f\x91\x84\x10\x81\x1d\xb8:\xb7E\x169\x13\xa7-\xdf\u00a5u\xf9\xd6\xc0T>\xbc\xf0W\xba\xec \xe5]6u$\xf7\x96X\xb7\xb9\x00\xeaN\x8eZ\xf9\xcc%\xb5\x0f\u0228\x8b\xe9\xa03\xb5\xd9Mi\x96-\rn% \xa5A\x89\x06%\x16b\u024c\xd4R\xa5g\xa7\x8e\xdf\xd5&\x89\xa1\xfd[ph\xdf&h\xadlg\xee.~!\t\xb5Z\rk\u05acy*\xfd\x99w\xbd\xf3]'W1\xb7\x85E\xef\\\xb2d\x91\x17\xb8\x90\xe3\xd1\xfb\xf6\x0fb\xf7\x81C\xa0 \x82f@\xb1\x80rFF\tK/\x1c\xf8\xd8\x13\xe5\xa7[\xd0\x05\x18P-\xf4\xd6B\x9c\u007f\ue648\xa2\bR\n\x04%B\x9e\xb2!c\x8e}s)~\xcd\x05Iq\x97\xf72I!?\u0582/R:\x90\x90Xu\xda*\x9c\xbax\x81\r\u0560\xce>\x94\xa7\xdeEg\xf1_\xed\x16\x00\x85e\xe1-ak\xe5%\xbbH\x85\xccc\u0734o\x8cL\xe5FbT\xc0\xf6\x8b\x85\xbf\x9dr\u074d\x9b\x9e^\x8dqb\u040a5bm\xa0a`\x88\xed \xd3a\xe0\xf6s\x03\"\x8d\xb9\xd10\u039e\xb1\x03\xa7\xf6\f\x02\xd2 \x11\xd6\x02V\t\x82\x16\f\x0e\x8a]\xb2\b+\xa8\x1f\u0687=\xcf=\x00U\x1f\x87\x90\x01RoXf\x83\x9e9\v\xb1\xe0\xbc+Aa\bE\x06p\xf3\x13!$\u0205@\x93\x8b\xe31.~\xad|\x97EE8\xa2D$D\xe8\x92\xcc\xc8\u0716\xe1\xc6m;\x1f\x9e|\x95\u6a7e\x99/<\xa9\x17LZ\xc8ar\x0e<\x19\x9d\xd9\x05\xb31\x99\x0f\xba=^\u02024\xc8[\x10\xb3\xe1gz\x92\x05ap\xf7\xabh\x8c\x1d\xca-o]\xf7\"\x84\xc0\xa9\xa7\x9e\x8a\x8b.\xbeh\f'\xe9C\x00\xc09\xe7\x9cs\xa8V\xeb\x19I;]\xbf0\xed\u067d\x1bol\xd9\x0e\x11F\x0e\xf2\xb0)6\x99y\x0e\u02b9\xe6o\xd5@\x94\xb5BU\x18\x9c\u007f\xe6J\xac\\\xbe\x14Z\x1b\xd4*\x95\fC\x9f\x0e\xe3\xa4\x1d\xf2(\x13\xfc\xb4\x1f\a\xff\xdf\u077a\xff\xa9\x86\xa4\xed\x8bF\xf6\xbaB\xa0\u054a1{\xde|\xac\xbd\xe4\x02T\xa5U$\u0494\v\\\xb7\xed4g[SP;^z\xfcA!\u04c3y\xb8\xa3\"p\xb7\xad{\x17\u0225\xd0\xd9Sw\xbdj\xc7\xf2\xec<TZJ\xa3\x99h\xc4\xca\xc1e\xf0\x9c\x18\x05`\xa4\xfd\xdc\x00\xd0d;\xf6\xd9\xc18\xce\xed\u0749\xd3\xfb\xf6AV\f\x1a2\x80\t\x00\x13\x10L\x90\xd3\xe0HH\x18\xad\xb1\xeb\x89\x1fbd\xfb\xeb\xa0 \xcc0v0C\x86\x11\xe6\x9cq!f,Y\x05h\xab\xb2\x04\b\x90\xd6\xdeV\xc8\xc021&\x9b\x00\xfb\u01e0l\u0562\x1cb\xf1\xff\xdf\xf9ze\xe9\xbcT\x84E\xb9K\xe5\xa6.\xa0V\x99\xa4\x97;M\xbe\xd2,Tc48\x8d\xd6\xf3\xa0\x99\u0531\u04a43\n\xff\x85\n\r\x02A\xab\x18\u00c7\xb6\xa3\xd5\x18\xb7B!\xb2\xac\x190#\x10\x01.\xba\xe8\",Y\xb2tN\u0588\x1e\xd8\u007f\xf2\x15\xf3\x9bn\xfa\u012e\x993g>_\xadV\v\x85\x8b\b0q\v\xeb_y\x15\aG\x1a\x90A\x94y@\v6\x1e\xa6k\xcf\xde[\xc9\xcd'\xef\x06O\x9a\r\xacX4\a\xef\xbf\xf6\n\x1b\xfe`\fz{z\nR\xfb\xfco\xa0)\vy\xd9\xd7\xcb\x16\x81\xe9`\xe4Sa\xe9e~.\x96k\xaeq\xf6\xd9g\xe2\xf2\v\xdf\x06\xd5j8/\xe7\xee\x1b\u074ecCE\xf7C?\u00ac\fu~\xabe\x14\xbe\xea\xb3\xe8\xc7RfH\x8bB\xf9.\xff\x9by\xca\xe3\x90b\xf2\xc6\x10Z\x89F+\xd6HT\n\xa7\u5fd7Hd\xfe\x1e~\x97\x9f\x9a\xc6\xf5\xc9&\xde\u05b3\x1bg\xf6\xecA5J\x90\x04\x12:\x12v!p\xe7KTj8\xf4\xfa\v\xd8\xfd\xf4O\xa0\x93\x96\x1d\xcei\x9d\xe5\x99\xd6f\xcf\u01d2K~\x01\u044c~p\x1cC*\x9b\xedIA\x88 \xeaAX\x9b\x81 \xea\x01U\"\x10\xdb\xfc\xcbT\x00\xd5^c\xb9\xc4\xd1\u0187\xc1}\u031b\b%\xfe\xc4\xed\u017bDB6i\xf1\xc7\xf4a\xba6a\x13ke\xad\x12\xc8\xd9%\xb8\xc5.\xb3Vg\xbbS\xe2\xc22OY~if\xeb/\x04\x9a\x8d\x11\xd4G\x0f\xc3h\x95\xeaNA\xb0\x011Q%\xc2\xd9g\xbf\r\xbd==k\x98\xb9\x17\x00\x16-Xx\xf2\x15s\"\u06bfx\xf1\xe2\x17\x97,Y\f\xad\x15\xa7t\xba\xf4\f\xbf\xf0\xc2\xcb\u0630y'\x82\xde>$\x06.C\xb2\xbd{x\xeb\x1f\xe9\xefH\x92\x04Q(q\xdd\u0557\u0f37\xadF\xbdaC\x97\xa3\x8c\xe125d\u04ad\x90\xfb?\x9f\xb2QR\xdf\xf22\x1c\xbd\xfdg\xa7\xea\xc8\xcb\x16\x0e\xa5\x14f\u03dd\x8b\x8f\xdf\xf0n,\u83e0\x9d{e7Z_g!\xf7\x90f\xf2h\x89\\\x84Zh:p\xcdQ\u0737\u075e\xe7g\xc3\xf8\v\x89\x81\x80\xcaF\x8f\xd41\a \x94\xd1]=\xdf\xf16\x90\x80J\xd0[\u0144\x96\xd2h%\x06\x8960i\xf7\xe7\x89hR\xf4\xc1\xea(\x9c\x96\xc2\xc0:\xfc\xb9\xc56\"\x85\u0555}x[u\x17*Q\x82$\xb4\x8d\x02k\r\x11U\xd1\x1c9\x82]\xeb\xee\xc5\xc4\xe0\x1e\x90\b\\\xa1\xb2\x1d\xa5\x90\x01\x16\x9c{\x05\x06N?\u07fe+e \rA\x86UT\xfaf\x81a0\xb8\xfd\x15\xec\xd9\xf2\f\xc6G\xf7CBXO\x16\xe7\u007f\u4670\xa3\x8c\x8a\x98\r;}\xaf\xf7)\xb8\x9e\xed;\"\uee8b\x9atDZ(\xd4\xe5\xa3j\u01ddJ\x83\xa6\x8d\xa3x:\x98\u02a4\x10Jz\xdc\xe1\xabx\xd3y\x1c\xb2\x9cU\x16\x9cE\xf0%q\x1dq<\x81v\x034f\xc6\xec\u06730\u007f\xfe|0p\xca\xfe\xc1\xc1\x15'%\xcc\xf2w\u007f\xf7\xb7\x11\x00\x9cy\xe6\x19\x8f.[\xb6\f\xb6\x86\x11\xfb\xc6Q\x87\xf6\xec\xc4\xf3\u03ff\x88Fl d\xd0&\b\xe1\xd2\xc1\xd4[]\xd0\x1b\xf5:V\x9f\xba\x18\x9f\xb9\xf1}\xe8\xed\xa9adtlZ\x01\x14~\x10s\x19\x9c\xe2\x17\xdd\xf6\xe7v{\xbd\xa9:\xf5n\x98\xbaOO\xfc\xf0\a\u078dw\xad=\x17\xbaU\u03feF\xd3=\x9e\x85y\x11\xb51\x02\x90m[\t\xc7&\xe3'\x0f~\x9d\xee\xf3E\xa1\x88\x13\xb4#\xfc\t/\x98\xba\x9b\x11\x17M\x8d\xd2vl\xfc\xc9)\x00\x13\xa5\xd0J4b\xcdP\xa6\xe8]\x9e{\x85pV\u0233L\x06\a\a\xe4\x9f\x03\x01i\x9c\x1a\x1c\u009ap?\"RPL \x19\x02`\xecy\xea>\fn|\x06$\x84\x83\x0f8\vH\u8677\x18K.{/*3\xe7@\xc7-\x04\bP\xad\xf5\xa3\xda7\x80\xc3{7c\xddm\u007f\x8cG\xbe\xf3\xdf\xf1\xcc=\u007f\x81W\x1e\xf8&\x0e\xee\u0610\xf9\x9bP\xfa\x9e:'\u02c5\x93g\u00d7)\xd3\xf90g\xc1B\xc5\xed\x19O\x1f\x18+37\u0387\xa2\xed\xab\x03\x95\xee\xf4\n\xbf\xde0\xd8\x1bhZh\xcb\x0e\x94\xdd\x06\x06B\n\xab\x1b\x91\x00\x02\xfb|f\x86\xce1\x00\xb7x1\x94N\xa0un\xbf\xe0\xdfS\xbd\xbd}\b\x82\x10\u0198\xc0\x18\xb3\xf8d,\xe6\xc1\x19g\x9c\xae\x00\xe0\u04df\xfe\xe5\xd7\u05ed{\xfc\x95\x81\x81\x81s\x87\x86\x86\xb5\x10\"\xf0\xb9\u044f>\xfa\x04\xae{\u05d58g\xc5B\x98\xc6h\xc7\xcdM\xc7\xd1\xd1\x1d\xed\xf6=\xbd\xd0T\x92\xe0\xfaw^\x867\xb6\xec\xc07\xfe\xe5.\u0109\xea\x1a\xbe\xdc\xceR)+\xc8e\xf0J{`\xc7d]\xf7d\xde\xe6e\xbc\xf5\xf4\xf3+\xaf\xb8\x14\xb7\xfc\xe2\a\xd0\x1b0\x92\x89\xb8\xa4\x93\x9a^\x05M3\x1f\xb9\xacu\xe5Ni\xfdQ/\xba\u053dw+{rj1\xe2\x17x\xea\u03a1(_@\u0494\x9f6V\x8f(\xbc\x0f\xb2\xb1kJ#I4\x942\xce\xf8/}\xae\xb7#1\xb9\xb29+\xdc\u0739\xb0\xd8\xee\x92Q\x81\xc22\x1c\xc6(j\xd8&\x17\x82\xc3\x1a\x06\xd7?\x86\xed\x0f\xdf\x01\xd5\x18w\xb0\x8e\xce~JFU,\xbd\xecz\xccY}\x1e\x8cJ\x10\xf5\xf4\xa3\xda\n\x11\x1f\x1e\u0096\xf5wc\xfdO\xbe\x81\x83\xdb\xd7C\x06\x15\x04\x95\x1e\x1c\u067b\tZ\u0168\xbd{.f\xcd\\\x04\xc4-\x97\r\xea\x18\xd7e\x91vm\xc3\xc2\x02\x15\x9b\vzN\x97\xf9Yf\xa4\xc0%M\xb9\xe7\x8cE\xed{8*\xc1\xdcK5\xa4\xb9\xf5\x1e\u5db4\fc\xbf!\xad\xb9\x98\x88\b\x81\x04\xa4\x84\v\x87\xb7_W*\x8fLd'\xf6\x82\xb1\xdd:\t\x99\xbd\x96/\xf3\x17\x82\xd0l6\xa1\xb4\x02\x119\u01d8\x93\xb03\u007f\xc7;\xae5\x1f\xfa\xd0\r\x92\x88\xde\\\xbdz\xf5\xed\xa7\x9d\xb6\nJ%$\x84(\u063an}\xf5U<\xfe\xf4Kh*\x03\xc8\xc8c\xb1\x14/$?\xaa\xeb-\x85\\\xc8\x0eDf\xd4*\xf8\xfc/\u007f\f\x1f\xff\xe0\xb5\x16\xcb\x14\x02A \u074a/\xa6\xd5A\x97\r?\xa7\xd3}\xd34(\x91\x9d\x03R*\x18\xef_|\xd1\xf9\xf8\x83/\xfc\x1aV.\x99\x87\xa41\x81\"1\xef(\x8e\x87o2W\xb2\x9aNWV?9\xccB]Xp\xd4uqH\xa5\xfd\xb2\x00\xa1\x14\xa5\xfe<\xe5\xfa\xc1\x93\xa2\xea\x86\x19\xb1\xeb\xc8\x13m\xd0NXJ\xc3s\xb4\xb1\xf4S\x93u\xe6^A7\\$\x8b8\x88J\u00a0\x0f-\xac\xa0\x83X\x1c51\xba\xf3\rl\xba\xf7\u007fcl\xdf\x0e\x80lt\x19\u0600\x84]\xac\xe7\x9f{9N\xbd\xe6C\bzf \xa8\xf6@\xd7'\xb0\xfd\x99\x1f\xe2\xfe\xff\xf5y<\xf0w\xbf\x89\xfd[\x9e\x87V\tT\xdc@c\xf4\x10\x86\xf7m\xc1\xf6\x97\xeeG\xfd\xf0V\xf4\xf5VQ\r\x05\xa2\xc0\xfa\x10Q\x19w\\x\xe1q\x18\xd1\xfd\x00\x00 \x00IDAT\xd7\x1eP\x9e\x96\xaf\xa2-.\x95br\xdc\xd1aSgW\xc0m\u06c2\x12\xdf\x00\x9f\x19\xd6\x16\x14W0@3\x86\xf3\xfd\x18\x01\x14\x10\xc2~\x89hN\x80\xb0*\x10HB \ba@\x90!\x15h9\xe9.\x93\x89\x11\xd5z\x11Vz\xbc\xeb\xdc\xcd!H`hx\b\x87\x0f\x1d\x063\u01f5j\xed\xe0IY\xcc\x01\xe0\xf3\x9f\xff\x1c\x03\xc0\x97\xbe\xf4\xfb\xff\xb0`\xc1\x82W\xe7\u035b'\xb5\xd6I\x8e\x9d\x03\x80\xc6c\x8f?\x8d\xbdG&\x10Uk@\u6652\u039a\xf3<A\xf1\x16\x16\xf4\xc2n\x93\b\x8dF\x03\xf3\xe7\xcc\u0117~\xe3S\xf8\xc8{\xdf\x01\xad5\x946\b\x83\xa0\xc0\x97'J\x83e\xb9k!\xf7\xc39\xa6b\xaf\xf8,\x98\xc9\xf0\xf9\x82\x0f\xb3\x10\b\x03\x99\xdd\x17\x97^|\x1e\xfe\xe8\xf7~\x1dg\xaf>\x05\x8d\xd1!\x90N\n\f\xa1\xa39\x16>\x83\x8f\xa9[\x97{l\xbb t \xa3\x9dI\xa2\x9d\xc8k9y\x90\xc0^h\fO\xb9\xab+[\u0532m<\x01\x86\xad\xaa3Nlr\x931~\xa2\xbbU\x19\xb2a\xe8\xf4\x1c\xc1\xb3d5\x1e\u0312\x0eA}\xa6Qv\xae\x193\"\u00bc\xe6~\xec{\xe4v\x1c\u07ba\x11B\x06\xd9@\u03fe\x9e\xc6\xccS\xd6`\xcd\an\xc1\xc0i\xe7\u00a8\x18\a6<\x85\x17n\xfd\n\x1e\xfb\xdb\xdf\u014e\xf5\x0f\xd9\u008f\xd4w[g\u007feR\x1fA%\x14\xa8\xd4$d\b\x04\x92\x10\n\x82\xf4\xb8\xe4)\xcf=KXB.2b?j\u03c3\xda\v;\x9aB\xc25M\r\xb3L\x87\xcd@\xe5\v.\xda\x03\xa6\xdd{c\x00$\t\x95\x9aD83\x80\x9c\x19@\xce\b@\x91\xb5\xb0\x15\x82 #\x01\x11\n\x8b\x97S\xbe\xa34l\x10\xf5\xf5\xa3\xda7\xdb\t\x8cR3\x17\xfbs\xf5\x89:\xb6n\u074aV\xb3\u065a=k\xd6\xf0I[\xcc\xdf\xfb\xde\xf7\x9b\x83\a\u007f\x06\"\xdaw\xdey\xe7\u07fex\xf1\"\x18c\x82\xf6\u0405W_Y\x8f\xf5ol\x87\x96\x15\xb0\x88\n\x81\xc3\xfe\xb0\xe6X\xb1\xd9c\x83]\x18\x13\xe3\x138e\xe1\\\xfc\xe1\x17o\xc1-7}\x00RJ\xb4Z\xb1\u06c2\t\xaf\xf0\xa2t\x88\x99\x16\xe70\f2FL\xfa\x9ct\bZV\u0627\x1b\x19GD\b\x83\x00Q\x18@\x1b\x03!%\xde\xf7\vW\xe2\xff\xfa\xaf\xff\t\x17\x9d\xb9\x1c\xad\xf1\x11\xc0$v\x11\xe4\xe3\xc8\xfa,\xe1\x06w\u04d3L\a\x06\xa3\xd2\r\xb6/\xe4!\xb4k4\u065b\xa4\x14\xf0m?K\x93\xa6\u0388\x9d\f\xbeI_[k\xb6<\xf2\xc4@i\xce\nq\xdaYg\x10\x8a\xd7yk\xb6\u0676\x99\xba7\xed\u0239\x04KwEM\xca\x00\xa1\x14\xd8\xf9\xe2:\xecy\xfe\xe1\u0316\u0546Y\x18\xb0V\xe8_r\x1aN\xff\xe8\xe70c\xc9*\xecy\xe6\x01\xfc\xec{\u007f\x8d\xe7\xfe\xd7\x1f\xe0\u0347nCc\xfcH\xe1\x04\xa5@Xz\x8d\xac\xb9\xecz,Ys!\x94j\x81\xa2\x00\x14\x10\x84\x04Bi=\xd2\xf3\x18{r\xd0\x05\x15l\xe3\v\xd4\xe0\x8ek\xa0\x18p\u0285E\xb6\u06d5Q\xb6\x9cs\xd7U\x9e\xdb!3\x8f\xff\x9d\x82\xf9i4]\x10\x10\xc2^\tY\xb5\xa1\xe5\xa8\x12\xb8O\x80+\xb6\xe0KA\bB\x02\x059L\xc3l\a\xcf2\xac\xa0\u007f\xeeR\x04Q\u0545h\xa7\xf5\\@%\t6l\u0600\xf1\xf1\xf1\x04@|Rb\xe6\xe9'\xf3\xe6\x9d\r\x00\xf8\xf2\x97\xbf\xfc\u05ef\xbc\xb2\xfeS\a\x0e\f\xae:x\xf0 \v!\xc8hmW\xc7\xc68\xee\u007f\xe0a\\\xfe\xf6\xf30/\xea\x81i$n\xa8E\xe0\xe3\b\x1cFigw\x14\xc396\x18\x9b\x98\xc0\xbc9\xb3\xf0\a\xff\xf9f,\\0\a\xff\xf8\xdd{\xb1\xef\x80\xddm\x05\x81\x8df\xb3\xa2!\xd3u\x90g;;sTC\xce2\x1c<\xe5\x8e\vB\xb6c\xd1Z#I\f\xe6\x0e\xcc\u00a7>\xfa^\xdcr\xd3\xf5X0w6\xc6\xc7\xc6\xf2N\x9c\x8fn\xd2\xd0\xce5\xeff\x99;\tb\xda\xf5\x98\x97\ty\xca;u?\x8a-\x85[\x8aT\xc0b\x01\xe0i_\v(\x81s\x00\x02\x1b\x03e\f\x942P\x86a<\xd6\n\xbcn.\xeb\xb8\xfd\xee\u06dd\u007fk\x16\x87,t\xc5\xd7\xe5\x14\xf1X\x81J%\xc4+?\u06c8\xc7~\xf2c4\xc6G\x10\xc8 \v\xb0\x00\x00Y\xa9\"\xac\xf5\xe1\xf0\x1b/a\xd7\x13?\u0111-\xaf`\xfc\xc0.\xdb};\xac\x97\xbd\xa0\x8a\xec\x98\n\x81\xb7\xff\xe2o\xe0\x1d\x9f\xfdo\xe8\ud7cf\xf8\xd0((\xb0v\xbd\xc4\x02\x82\r\x02\x10X[H(u\xea\xed\xd8y\x15`\x95\xfco\xf1=\xc8;\x05}\xdcf`E\xe8n\xb0P\xb6?\x9b\xfc\xdc1\xe5\xda\x02\xe3\xbar!\x80\xa0B\x10\xbd\x02$`=\xd0\xc1@\x85l\xe6\xed\x98\x01\x9a\x1a$\x800 \xb0\x01\xb4rW\x98]\x851g\xe9\x19\xa8\xf6\xcd\xc2\xd8\u1f50A\xba\xc6\x11\x12f\xbc\xfe\xc6&l\u07f1#:\xf3\x8c\u04eb'mg\x0e\x00_\xfb\xda\u07e4\xc5\xe9\xc85\xd7\\\xfd\xed\u014b\x17AJA\x00Xx\xfc\xedg\x1f_\x87\xa7\x9e\xdf\b\x84\x15@\xe4~-\xbe\a>\xb7\xc1q\xddvle\xf9\x90|\f\x85,\x1d\xfc\x8d\x8fO \x8a$>\xfb\x89\x1b\xf0\xe7_\xfe\x1d\\\u007f\ud568U+PJ\xbb\xf0e{s\n\xf7\xc6|(\x85\x99\v\xde,\x19\xde\xea\xbc]\xcaR\x84\xca:\xf0J\x14\xa1\xbf\xaf\x17\xbd\xb5\n\xc2@\xba\"n\xe5\xfc\x97_|\x0e\xfe\xe7\x1f\xfd\x16~\xe7\xd7n\xc4\xfc\x81\x99\x18\x1f\x9b\xc8\xfbL\xe6)C\xe1\u0288\r\x93A)e\x86\xa74\xc5\xebLVP\xc9y\xae\xf8\xc3\u0254f\xa8\xb3\xef\x14\u007f\xdex\xbd4\x1d\x85B\xb8\xdb{\xd3\xc6 q\xb0J\xa2\f\x8c6\xc5\x01&\x17\xab\x9d\x0f\xa1\xd8N\x1c\x9e\xab\xa4\xbdn\r\xdb@\x04\xbf\x90\xb3[\x84\xab\x95\n\xb6\xed\u0703{\xee\xfd\t\xf6\xee\u0647@\x06 \xe3(\x8c\x9c\xefE\x87\xb7\xbf\x8a\xd7\xef\xfa\u007f\xb1\u3c7b1\xbe\u007f\xa7\xed\xf0\xb5\x82Nb\xdbE\xb6\x1d\u0359\x8b\x97\xe1#\u007f\xf2\x0fx\xdf\u007f\xfd\v\xcc^\xb1\x14\xaa\xd2\x02\u034d\x80>\t\xaaJ\xa0\"\xc0!A\b@z\xd3^r\x01\xcb\xd4n\x8a\xc5\xc5\x15=\x8dQ\xeb6-\xe1\u0098\xbam\xc8\xc9\u076e\x94\xac\x15n[`\u06f6\x87\xd9\xf6\x8b\xac\r\x88\xf3\x9e\t#B8#\x80\xac\x884\xe7)_mC\x02f\x04\xe0\x1e\t!\tR\x10\"ae\xfaLn\x80\xaa\rf/X\x89\xdeY\v\xb3\xc8>\x9b\xf4d\x11\x84\x83\a\x0f\xe2\xd1G\xd7U\x01\xcc:\x19\x8byV\xa5\xef\xbb\xef>\xe4\x9f\xff\xf8\xf9\xe7\x9e{\xe6\x86\u077b\xf7\u032f\xd7\xeb$\x84p\x1d\f\xa0[M(\n\xb0\xf6\xca\xcb\xd1\x1b\x00\xa4Z\xb9\n\x14>\u038cLxT&\xbb\xe6)\x98\f\u01c2\xa5\x83\bIb\xb7_+O]\x8aK/>\x0f+\x97/\xc5\xd8\xc4\x04\x8e\f\x8d\xa2\x15\xe7\x9e\xe4\x16O\x9fj/P\xc4\xcdE\xdaq\xb7a\xf1A \x11\x06!zjUDA\x00\xa5\x12L\u051bH\x94F\x10\x068c\xf5r\xfc\xc6\xcd\x1f\xc1o\xdd\xf21\\|\xee\xe9H\x92\x04\x8df\xcb.*<=4{2\xfa\xe7\xd1`\xe2t\x14\xc7sRn{\x16\xea-\x9c\x8f9\n\xe5\xc1\xef\u0667\xc2\u01e7\xfb~\x94f$Z#Ql\x87\x99\x1e\x94\xd2\x0eo\x15\x98*\xe9\x87\xc9\v\b\x15v]T0\xb8\xb1\x9d%\xa1Z\xaba\xff\xc1#\xf8\xce]\x0f`\xc3k\x9b!\x88\xa0\xb3\xc5\u0763X\x1a\r\xa3\x14H\xda\x0e\xd3\x18\x03\x9d\xb42\x8c\xdc\u007f\x84=3p\xce{o\xc2\xf5\xbf\xff\xe78\xfb\xba\x0f\x00\x14 \x9e\xa8[Ec(l!\x0f\xc8S\xef\xe7<x\xe3\xe9\x06:\xf4><\xd5\xe2\xdc\u05a3\xfb\xdc\xc7\u051d\xd3\xf7\x04\xa0N\x02{\xd1\x1e\x82\u0684`y1OU\xb6 \x82\x11\xd6\a'\f\x80\xea\x8c\x00\xb2O\u61da\xdbV[\x01P(l=\xf1h\x9a\xc6\xc1a BT\xe9\xc1\xc8\xc1\x9d8\xb8s\xa3\x1b<\xcbl\b\x9a$1\x94J\xa25kV?}\xeb7\xbf\xf9\x12\x00l\xf8\xd9\x06|\xfdo\xbf~r\xc1,\x00\xf0\xcf\xff\xfcO\xf8\xccgn\x01\x11\x8d\xddy\xe7\xbf\xfd\xfe\xe6\xcd[~8::\x9aejjG\xd3{\xee\xc9'p\xff\xe3\xd7\xe2c\xef\xba\x18\x11\xd5!\xd8\xf6h\x06V\x19\xea|z\\\u03ae\xbd\xf0L!\xee\xc4\xcfvo\xff\na\xf2h\x94\x92A\x98\xf7oA\x04\xc3\x1a\xcdF\x1ds\x06f\xe1c7\xbc\x1b\xef\xb8\xe2b<\xfd\xdc+\xf8\xc1\x8f\x1f\xc1\xc67\xb6\xe0\u0411a\xc4q\x92]\xb3\xa1sS\u0303c\xadC\x9b\x10\x02\x81\xb47g\x10H\x84a\x80@J0\x03\xcdV\vq\xac\x1c\xceg\xcbV\xa3i\vx\x14\x04X\xb0`.N_u*\xde}\xf5\xa5x\xe7\x15\x17\xe2\xd4Es!\x880Qo@+\x93\xfb\xb1\x1cC!o\xff&s\xf7\x8ez\xba\f\x16\x9e\xe2y\x9d\xb4F\xfb_\xe1\x06\xe1\x94\xf5\xe5m\xbcc\x86\xe7\x13s\xec\xf0\x9b\xd2\x06q\xa2\xa1t^\u023b\xc17&c\xaaX\x9a[\xb6\x8bB\x1e\x05\xc7\xce>\x04\x19\x14\x96\xe3\u0646\x81 \xac`\xdf\xc1a|\xef\xae\a\xf0\xcc\v\xafd\xe5Li\x95u\x85\xfe\xae\x10\xb0\xbb\x04\xad\x94\xe5B\x1b\xed\"\xcel\xd1\uf67b\x10\xa7]\xf5>\\\xf8\xe1_\u018a\xf3\u05e2\xda\x17A55t\x12\u06f8\xb8\xb4\xb0I\x003$\x10\x11DU\xc0\x8c\v\u0206\x06\xb7\fLb\xe07\xf9T\xb0\x9f\xf5\x8ac[t\\\xc7}\xd7\xee\x91\xce\x00\x89\xa2V\x81\xb8\x1b\x82\xce\x1e1\xb1Dn\xeae\x86\u06a2.\x11\bF\xb5F\x90\xfdd\xff>\xc3\x1d\x17Zv<%\x03}\xd2>o\x9c\xed.\x84\xac2\xd7h\x8d \x8c\xb0\xec\xac+\xb1m\xfd\x03\x18\x1f\xda\x0f\xe9\u03ad\x10\x84\xb8\xd9\xc4\xe0\xe0 ~\xfa\xf0#\xb7\x00\xb8\x15\x00\xce9\xfb\x9c\x93\xaf3\a\x80;\xef\xbc\v\x9f\xfe\xf4\xa7\xe8\x95W6\xe0{\xdf\xfb\xfe\xe6\x9bo\xbe\xf9\x8a\u077bw\xaf\x1a\x19\x19q~\xdb\xf6\xc0\xa9V\x03\a\x87\xc7q\xee\x05\x17`\xc1\xac^h\x15\xbb\x9c\x14\xeeX\xa9\x05\xa5\x1d\x1c\xa6t\x05i/\x06G\xd3E\x16;t\xbb\x93h%\x1a-C\xe8\xef\xeb\xc1\x19\xabW\xe0\x1dW\\\x84\v\xcf9\x03\xf3\xe7\x0e\xa0Z\x89\x10\x86\x12Ji4\\\x1a\x11{\x16\x9c\x96yb\v\xba\x10\xd6\x14\x89\xd9 I\x14Zq\x828I\x10+\x05\xad\xed\xb6?\b\x02,\x987\ag\xaeY\x89\xf7\\{\x05>\xfb\xc9\x0f\xe3W?\xf9!\\v\u1658\xdd\u07cb$\x8e\xd1h\xb62\x95 xz\x1e\xe54I\xa7\u0716\xa8\xd5q\x1c\u02e4\xf1S\x15\xf1\xc9\xf6+T\xb6s\xc9(\x87T\n\xed\x1co!\x87W\xc8\x13e\xa0M\x1b\xe5\xd3\xfd7E\xc7\xd8%c\xa5\xb4C\x98\u0734)\x03\fr\x97\xd7\xccy/\x9f\xd9\x11\u00a8\x82##u\xdcv\xefCX\xf7\xf4K0ly\xeb\x16.3\x05\xb0\"\xdd\xea\x1b\xada\x8c\v\x84v\x1d#\xb3\x81\x88jX~\xcd\rx\xe7o\xfd\x9f\xb8\xf23_\xc0\xa2\xd3V@\n\x89\xa4\x11\xdb\u039d\xa8\xb0\x9b\xc9p\xfb\x80@U\x01\x11\u065d\xa0$B@\x94u\xab\x85\xf9J!\u05c1\xa6X\xa2i\x92\xe99O~\xc6\u067f\u6f06\u0311\xde-\u00db`\xd2\u0667$ \x14\xa8V\tQ\xbf\x00E\xe4\xe6\f>\xc3\xc6g\x0f9\xf5\xa8 P(\xec\xb9L\x87\xdb\xee7J)Q\xe9\xed\u01e1]\x1b12h\xe9\xa1\xf6\xfc\x11 \x88\xe3V\x93\x84\x103\u05ad{\u426f\xfe\xf9Ww\x01\xc0\x0f\xee\xbe\v\xdf\xfd\xcewO\xae\xce\x1c\x00n\xbe\xf9f\xfe\u05b7\xfe\x15\x00\xf0\xb1\x8f}\xf4\xcb\x1b7\xbe\xbafdd\xe8\xd4\xf1\xf1\t\x04\x1c qE\xef\x8dg\x9f\xc4\xf7\xff\xed^,\xfd\xf5\x9b03\xac\xa2\x157\xec\x1c\x03\xbap\xf2\r\x8a'?s='\x1f\x9f+\u0798\xdd:\xef)\a\xa1)\xae\u02e9\u007f\x8cF\xac\x12\x8c\xaa\x04\x95\x00\xa8U+\xb8\xf4\xa2sq\xe9\xc5\xe7ahx\x14[\xb7\xed\u0126\xad;\xb1u\xc7\x1e\xec\u06bd\x17\x87\x8e\fatl\x02\x13\x13\r\x8c\xd7\x1bh\xb5b(\x13g7\x8f\x10\x840\fQ\xabV1\xaf\xaf\a\x03\xb3\xfb10{&\x16\u039b\x8b\xe5\xcb\x16\xe3\xf4\xd5+p\xc6i\xcb1o\xee\x1c\xabtT1Z\xcd&\x94\xd6Y 62\xc6\xca\xe4\u007fYG!\xa7\xa2\xa0\x8f<:0g\xdd9\x15\xc6ZeCOB\x99@f\xfa\n\xcc\xf4sS\xe2\f\x95E{u\x89\x8c;\x96G\xa2\r\xe2\xd8 q\x1dy\n\x9f\x14\xf8\x17\xe9V\xdc}O\xbb\xffS\xc7\xd5d\x1c\xd4R\x98\x9e\x17p\xdeZ\xa5\x82\xfd\x87\x86q\xfb\xbd\x0fc\u0753/d3\x14\xed\xf2_\x89\xf2\xe0\u2b10\x1b\r\xa3\x13h\xad\xb2\xfc\\f\x8d\xfeS\xcf\xc4\x19\xef\xff4.\xfd\xc5\xff\x84\x85K\xe7\x80\x12 \x99HlxEV\xccr;Z\xff\x9cQ\u0298\xe9\x11\x90\x01A\x84\x04\x1eW \xc1\xe0\x86\x81J\xf2\xa2^\x9e\xa1\xcd%g\x87\xbao\xb9\fw\xf2\x89\xdb\x02*\x88\xbaL\xb5\xc8\x19\x1e\x93o\xfae\xb3R\xab\x91@\xb5_\x02\xbd\x1aF%\x85\xd0\xed,!(C}(\xd7K\x10@\xbd\x02\xa4%\xc8%/13\x14+D\xb5~\x9cr\u0595\u063b\xe99\xa8\xb8\t#\x04\x041\xa4\x1046>\x81}\xfb\xf6\r|\xeb[\xffz#\x80'\x00\xe0C7|\xf8\xe4\x83Y\x00\x9b\xccq\xc7\x1d\xb7\xd3G?\xfaq\xbe\xf0\u008b\x9f\xfc\xf6\xb7\xff\xe5\xbf\x11\xe1\xdb\x0f=\xf4S0\xc0ZI2\x8e_\xfb\xc0=\xf7\xe0\xfc\xb3V\xe1\x86k\xd7\xc2\xc4\t\x88\x15$\x11B\xe8L\x05\xa6\u0754\xd5\x06t\v7\xec\xcb?'\U000b71b9Y\x97\x1f\x14>]@\"\u007f\xbeM\x92\xb1#\xb9\x04\t$\x94\x02\x8cj\"q\x98w\u007fo\x15\x97\x9cw&.\xbf\xf8\x1c\x18\xadq\xe4\xc80\x06\x0f\x1d\xc6\xd0\xe8\x18\xc6\xc6\xea\x18\x19\x9f\xc0D\xbd\x89X\x99lK/\xa5@\xb5RAoo/f\xce\xe8\xc1\xbc\x81Y\x98;g\x16\xe6\xce\x19@\x14\x06H\x94\x82N\x12$\x8d:\xd8(\x10k\x14\x93QyJ\u018ao\xc91\x15\xbd\x93K\t0\x9d\x0e\xe8\x93\xcd*\xcaX-\x8c\xa9\xb8\xdf\xe5\xfb\xa9N\\\x9f\x8f\xbf\x90;\x8f\x15m\x8a>+\xec\r\u074d[\xe5\xd8+\xea\xe9n\xb0 i\u271cW`\u007f\x00\x10$Q\xadF844\x8a\xef\xde\xfd \x1e}\xeaE\x18c \x85\xcc\x02\xb7\x995X\xbbW%\x8b\xe3\x1a60*\xb1\x96\xc5\xeeQ\u96cde\xef\xfc(N\u007f\u07e7p\xfa\u06ef\u00ac\xde\x00z\"v\x05\x93;\xa9(\xdc~ls:!\x81\x80\x88\x80Y\x04\x84\x04!\x05$'\xd0F\x83U\xdb\u0312\xf3\xb3GGA'`\ued01!t\xb2f\xca2~\xb3\xb6\xc4\x17$\x11\xa0% #BuF\x88\xa0\x9f\xa0\xc1\x80&KQ4\\\xec\u0385\xb0Ceo\xdea\xbb'\x80\xfa\x02H\xc3\xc0\xb8\x86\xd66\xf4\x03\x82\xb0`\xf9y\x985\u007f\xb9\xc3\u0383\xec\xaf\x0ed\xc0\a\x0e\f\xd2\xd3\xcf<\xb3\x96\x99{\x89h\xe2\x99g\x9f\xa6K\u07fe\x96O\xbab\x0e\x00g\x9ey\x06\xff\xcd\xdf\xfc5\xfd\xf6o\u007f\x81?\xf1\x89O}w\u01ce\x9d\x17\xef\u0739\xf3w7m\xdaDA\x18\x98VK\v\x00\x18?\xbc\x1fw\xdc\xf9C\x9c}\xe6j\x9c\xbeh&Z\xe3#\b\x1c\x13 \xed\x92\xd1\xfd\xb0\x02\xd7\xd1\x11\x15\xbe\u05ae+$.z\xefOBq\x9d\xa4(rF[\x94\x99\xb0\u0240\xb4\xb6)\xe0\u0292Qc\x87\xeb\xcf\xec\x8d00s)\x02!!\xa4\x04\x89<\x88\u00f8\xad\xb0v]\x85a\x86QvK\xad\xb5F\u0728\xa3U\xf7L\xf5\xc9\xce\x0e:\xcb\xeb\u051d*\x95\u031e|\xeb\x84BG\x0e\x94\xda'\x91\x1757\x99\a\x13w\xe9\u0727\xc2\xdd\xfd\b\xba\xf6\xe1X\x99\xac\xe8\x98\n\xb9J\v\xb9v\x18\xb9\x1d`\x1a\x8f\a\x9e\x0f=\xdb\xff>G\x93\xe4\x1c\n!/\u07cc\xbc\x95\x92a\x17\xf7j-\xc2\xc8\xe8\x04n\xff\xe1#x\xf4\xa9\x97\xa0\x8d\x81\x14\"\u03cf\xd5\x1a:i\x02\xb0.\x9d\xa9\xb1\x96\x0f\xf9\xf4\r,\xc6)\xe7\xbf\vK\xdf\xf9a,\xb9\xfc]\x98?o6\xaa&\x86i6\x8b\u0175=z\x8d\\0\xb2\xdf\u9ca7\xaa5\xb0.\x8f}\x01H\n\xbb;PV\xcd*\xdc\\\xa0]\xb2\xcf\x05\x1f\xfb\x829BfN\xc5\x1e\xbe\xcep\\u\xea\f\x15\xa4.\xe6l\xedWX\xca}\u05c2@\x01\xa1\xd6\x1b \x9c\x19\x80\xab\x04\x8auf[\x9b*XS\xc8\xd1\xe6'pg\xdc)\xac\xc8H\xf6\x05\x16F\x9b\xb0;\\\x9d\xc4\u86fd\x10\xa7\x9c\xb1\x16\awnty\xad\x96\xa5&\x03I\x83\x83\ay||\xe2\u04bb~p\xe7\xaf\x03\xf8\xab\x17^xA\xb8\xbe\xf2d,\xe6g\x03\x00\xdf\u007f\xff\x8f\x03\"R\xcc\xfc\x95#\x87\x0f_1>>~\xe9\xb6\xed; \x85\x84v\x93\xfa\xf5\xcf=\x8b\xbb\xef{\x18\x9f\xbf\xe5\xe3\xa8Uj\xe0\xa4\x01C\x021\v\b\x17#f\x03~\xdd\xc5I\x1e\xc7\xd7\x15wIE\x8e:Q\x9b\xf7\x86\xe7?Q\xec:i\x12\x94\x8f3\x9f\x88\x00\xda+7E\xdd;9\v\xce8\xd6\xe08\xbf6\v\xc6U~\a\x82b\xcee\xfb\xfb\x05\xf9\xdd :n\x88\xc9\x17\x1f*\xfa\x17qI\xa2\x10\x8a\x05\x9e\x8fb\x91\xa3i\xfe\x9bJn_.-\xf2\\p\xe9\xa0c\u0499vB>\x896\x88U\u0691[\u06a0q\xaaM\xe31\x1c&\x03\x17l\x01\xf7<N\xb2_\"\n\u007f\xb7 B\xb5Z\xc1\xb0+\xe4\x8f<\xf5R\u0591\xb31PJ#\x8ec\x18\xd5\xc4\u0095g\xa1w\xe1\x1aL\x1c>\x80\xe6\xd8\x11\xb4&\x86\xc1\u0318\xbf\xf2\x02,\xbf\xf0:\xcc=\xedB\xd4V\xaeFu\xe9<\xf4\x06\t*I\u074a\xee|\xba\t\xba\x0f&\x88\xda\xf6T\xfe\xf7\x8d\xbb\xcej\x02\x8c\x10\xb1b\x18V\x88Z61\x89\x8d\xdf\xf8\xe4\x05\xddU\xe9\x8e3\x96z\xb6p\xfb\xc04+\xe8e\x9e\x94\x93\xc08\xc2\xdel\xc6\xf9\xafT{\x02Tf\x05@\xcd\xd2\x10!\x03\x18\xa1@Bg0hv\x93\t\xb8@haC+\x90[2\b\x02(\x12P}\x12Z\x19\xc8&\u00e8\x18Q\xb5\aKN\xbf\x04\x9b\x9f\xbb\x17c\xc3\a]XE\xba\x83\x96j\u01ce\x9d\xe1\x03\x0f<x9\x80\xbf\xfa\xcd\xcf\xffg\xbdc\xc76:\xf5\xd4\x15|\xd2\x15\xf3\xf4\xf1\xeew\xbfW}\xf1\x8b_\x14D4\xbai\xd3\ubffcs\u05ee\xfb\xeb\xf5\u01b2\xfd\xfb\x0f\xc0\x187\xa9W-\xdcu\xfb\x1dX\xb5r9>x\xedZ\b0t\xdct|]\x01\x05'*r\u05f2L\v;Q\xdeC\xb1\x87\xc0\x12Cp\xfb\x8d\u0649\x01s\x89/7\x95l\xffDv;{\x8aEo\xf9/\xa4\xbb\xa4-qfND\x9eq\x90\xc9::fn\xebx\x8a\xfd,\xe5\xab\xd64q\xfe|P\xdcm\aRz{\x95\xee\xa4;\x91\xe2\u92b1:\xed\x93\xfc$ .,h\xd4\xf1\f\x00\xc7\u0451\xa73\x81D\x19\xb4\x94\xe3\x90;h\xc5/\xe4\xc6\xf8\xe8\xbc\xd7\x15\xb2\xb7\x843w:*\xb6\x1fC7\x03\xa9U+8ph\x18\xb7\xff\xe8\x11<\xf2\xf4\xcbH\x94\x82\x94\xc2v\x80Z#Nb4\xeb#Xu\xd6y\xb8\xe2\x96?Fm\xfeyH&F\xa1\x9bu$\xad&d\xb5\x8a\xdeY\xf3Q\x9d5\x0fI_\x15I\xb5\x85\xaa\x19C\x0fKH):\xad\xc2\u0476\xe5\xccL\xc9\v\x88\xb9-\xec%\x97\x97\x00 #\x01\xd5\x1fX\x99\u3a36A\xc9\xcc\u0747\"]\xc06.1\xcf\u028b?\xb5\xdda\u0735=`\u07cf\\\x10\u00aa@uf\x00\u0457\x97\x17!\x05D\x108K\\\x06\x9c\x101SQ\v\x91N5\x1c>\x96\x0f7\x04\x11\u008aD2\x03`N`Z\f\x95\u0118\xb3h5\x96\xae\xb9\x04?{\xf2N\x04a\x04\xc3\fA\x84\xa8R\tv\xee\u0705\u077b\xf7\\\xf7\u063aG\xae\xbe\xfa\xaaw<v\xff\xfd\xf7\a\x00\x92\x93\x86\xcdR\xf6\xf8\xc1\x0f\xee\xc0W\xbf\xfa?\xf1\xb5\xaf\xfd?\x87\xff\xe6o\xfe\xaa\xbee\u02d6\xf7\xee\u06f7O\x1a \x9bL\xb7&\xc6\xf0\xfa\xe6\xadX\xb6b%N[~\x8a\x1bt\xe4C\x1e\x86\r\u007fV$2 NQ\bC\x12\x02\x8c\x904\xa4\xbbx\x05\x8a\"#\xe6\xe9a\xe5\xd3\x19\u06b5s<hj\xba\x06:\x8d>\x8b\xa51\xeb\xbb\t\x8e\xdf\xd5\xd9\xc1\x94\xe1\x91\xe9\xff\x05\xca\xe7N\xfe\x93\x89<\u04e5\xb6\xf8\xb4no\xfehL\xb5\xa8\u02f3}\xc0\xc4x\x8e1\xe5\xf5\xe2\xf8#\x03\x13\xa5\xd1Lre\xa7\xf6?<c\xac|~\xde\xe9r\xc9m\uad54\xa1\x92fEZGB;\x9b\x89\xa2\bo\xee\u0707o\xddy?\x1ey\xe6e$\x89r\xbe)\xec<~4\xe2f\x1d+W\x9d\x86w\xfd\xca\x1fa\xf6\xeak!\x83>\xf4\xf4\xccF\xdf\xecE\x981\xef\x14\u0318\xb3\b\xb2\u0683\x96L\xa0k1\xfa\xfb$\xfa#im]SC\xac\xec\xb2)\x02\\\xcc\xe4\xe5^\xe6\uf66d\xd9K\xa6\x1a%\xf8\x0ev\fA@\x18\n\xe8\x90\x10K\xb7P\xb9\xc0d\U000b72e5K:\xa1d\x85ikh\xdaH.\x1d@Z\xfagP\xceZqL\x96\xa0G\xa26\x10\"\x9a\x1dX\xde}\xda\x12e\x17\xb1\xcf\x1a\xa3\x9csN\u0536fPA\xab\"\x84\x1d\x02+\x02X1\x90$\xe8\x9b9\a\x9c\xb4\xb0\xeb\x8dg\xa1\xb5r|s\xfbbI\xa2LT\tjc\xa3\xa3;\x9e}\xf6\xb9G\xef\xb9\xe7\xde\x13\x1dO\xfc\xef\xee1\xa5U\xe4\x82\x05\x8b\xf9\v_\xf8m\x00\xc0\xb5\xd7^\xf7\x8d\xab\xaf\xbe\xfa\xcf\xce?\xff<\xf4\xf6\xd4 \x82 +]\xfb\xb6m\xc1\u05fe\xf6u\xac\u007fu3\xaa\xbd}\x99:\x94\x19\x99BP\xb1\x84\xce\xf0\x03\xcbK\x97\x04\x04\x94\x1761\x9d\x81_\xc9\xd73\x93\xfb6\x1de\xc1\\n\x92B\xceSa\xf3T\x9c\xcar\xbb\xae\xb1\x8de\xd1\u0669\x96'\xe8LZ\xc81\xb9\u043a\x13\xbb\xc41\x16T.\xed\xda\xd2\x1c\xa9\xf4\xfc\xa5\xf9\x9d\xa6C.\xc2\xc7\xc5\\a\xb0\xed\u0213\x9cKn\x8c\u0166\xd3b\x9e\x1ac\x15\xfe>\xceY<\u0145Ix\xb517\x81\xb3X\xb1\xd5\x15T\xa2\n~\xb6i\x1bn\xfd\u07bdx\xf2\xf9\r0\xc6r\x95\xad(\u0206p\xb7\x1a\x13X\xb1r9\xae\xff\xe5/a\xe6\xcak\xd0\x18m\xc04&\xa0\x9a\x13\x88\x1bc\x88\xeb\xa3h\x8c\x0f\xa3a\xea\xe0\x19\x84\xfe\xfe\x10\xbd\x01\xb9\x05\x9a\xba\x9c\xb0\x9cuC~\xd7\xec\xfcGT\x12C\xb5\x9aH\x9a\r\xa8f\x03\xaa\u0544\x8acOIj_\xbb&\tsj\x12\xfd\x03\x01\xc4\xfc\x10jV\x80$\x12.\u06d4\u06ba\x1c.\x1d\xccPG\xf4\x1by\x83T*\xccl(\x03\x17\xdb\x1ayw\xc32\u0641g43@8+\x00\x82\xb4\xb1\xf1\x82\u02e5\x84\fB\x9b\x89 \x05\x84\x14\x16/\xf7S\xc1<\xf1\x96\xbf\xf4\t\x00\x91 \u022a\x04\xf5I\x84U\t21\x16/?\a\x8b\x96\x9f\x03\x157\xad%\x80\xdb\xc1\x85QH;\xb6\xef\u0091\xc3C\x1fd\x93\xac8\x190s9\x9d'\xfd\x97\xff\xf2\xbb\xb8\xed\xb6\xdb\x01\x00\x0f>\xf8\xe0\xcb\xeb_~\xe9\xc2\xc1\xc1\x03\xab\xea\xf5\x06i\xe6\xec\";28\x88m;\xf7b\xd5i+\xb1t\xd1B$Jyy\xb2\xe4\x8a5#\x00[\xb8\x85\x01I\x8c\x88L\xae\xb0\xf4\v3O\xd98w\xed\u01cfv\x19\x9e\nO\xee\xfe[\xf2\xff\v\x0f\x1b\x17(Wm\n\xbf\x1b\xa7\xeeE\x1c]\n\xbd\xefg\xcdDG\xfd\xbe\xa7*\xe59D+\xa1!]G.Jv64\xfd\xf0\x8ci\f;[\xb1\x85V\xb4\xb6]\xb86)[\xa4\xcd>\x98\xba\x9f\x8d\\L\xe8\r\xff2\x15\xb2\x84!\x89\u0219\xa9=\xf9\xc2\xcf\xf0/w\u070f\u05f7\xec\x00\t{\xedi'\xfaI\x92\x04q\xab\x893\xcf:\x03\xd7\xdf\xfc\x05TW^\x87\u047a\x85\b\xadq\x9b],\f\x01\xaa\"@\xb3\x03\u0318i\v\xb9\xec8 y\x81J'\v\xa9}\x80Q\n&I\xa0\xe2\x16T\u0482j5\xa1\xe3\x16t\x12\xc3\x18eM\xbc\xd2\x04#cR\x16v\xa6\xb4\x0e\x88P\x91\x02\x95H@\xf6Hk\u0125\xd3E\x8f<7[\xcam\n\xfcXXj\u03dc+\x0e\xec\v$\x84\x0e\x88\x8f<\x81\x90\xb5\xae\x8df\x85\b\xe7\x86\b\xaa\xd2m6J\xae,!,W\xde\xcb\a-t\\\xdc%\r\x89r\xd6M\x10\tDB\x00\xcd\x043\xfa\xe6b\xec\xf0^\xec\xda\xf2\x02\xac\xed\x88=\x97B\x10)\x95`\u018c\xbe\u015b7o\xfe\xfe\xbau\x8f\xef\xfa\xbb\xbf\xff:\xdd{\xef\x0fO\xeeb~\xdbm\xb7\xe3\x87?\xbc\a\xdf\xfe\xf6w\xf0\x95\xaf|\xa5\xf1\x8do|\xe3\xf9\xd1\xd1\xd1_\u077bgOPo4\x9cT\u06b27\xf6\xed\u074b\xdd{\a\xf1\xb6\xb3\xd6`\xe1\x82yH\xe2\x96\xdf+\xd9\x13\ufdb9\x12\x8c\xc0\xf5\xea\xe9\x10\x91\xdb:m\xfawx\u040aB\x1e\xbf\xb8qG\xca\x0eP\x12\x14C\xdd!$\x97\x96\xd5Q\xb4:\xdc\x0e3\t\xf6\x89)\xaa\xdc\xe6\xc3\xd1D\x88:\"\x00\u0085x\x1b\b\xb2;)ry\xaf\xe2x\xcd\xd5\x18\x88\xb5\xed\xc8\x13\xa53.y\x86\x8f\xa7|\xee.\u02d4\u007f\xdb3S\x86\u074a\\q`U\x88$`H\"\xa8\xf4@\x1b\x8d\a\x1f{\x16\u07fe\xf3\x01\xec\xde\u007f\xd0F\x02\xc2\xc2*Zk4\x1a\r\x04\x02X{\xd5\x15\xb8\xf6\x93\xbf\x05Zr\x15\x86\xc6\x00i\xf2\x80m\x12\x02,\x04tU@\f\x84\xe8\xeb\x0f\xd1\x13\tH'+\xb7]\xa6/\xcfLC\x8d\x9dB4\x8eaZM\xa8f\xd3v\u07ad\x16t\u0702Q\xca\x1ax\xb1K\xaf\xef\xf0\xff\xc9}|\xb2\x8e\x16\x80$B\x14\nD5\x89 \xb2\v\xaf-\xea\x1e\xba\xc3\u014e\x82D\x97\xe5\x9f\U000ee768[\xa3\x90c\xe5F\x10\xa4\x04\xc2\xfe\x10r^\x84\xa07\xc8]\x1c\xcb`Ka\x8fQjM\x9c\xedR\xb2\x85\x9a\xbc\xd0\xe7\xce\xf4\x1bA\x96\"L\x02\xe0\x86F\xc0\x01X+\xec\u0776\x1e\xf5\xb1#\b\x82\xa8x\xcf\x18\x8dE\v\x17\xecz\xfe\xf9\x17\x1e\xfdy.\xe4\u04c2Y\xd2\xc7\xfb\xdf\xffA|\xff\xfbVEu\xdey\xe7\xbfv\xd3M7~\xee\xdak\u07e5\xe7\u03db\x870\x8a \x830{\xee\xd3O=\x83\xbf\xfc\xdb\u007f\u00ae\xfd\x87\xd17\xa3/\xe3\x05\xa7%\u0278N/e\xb6h&hCP\u01a9\xc9\x18\xa5,\n\xfewV\xd0\xf3\xa8Fg@E\x9c\xdd\u0113\xc1F\xecc\xbfmE|\xda\xed3\xbb\x1b\x86\xf9h\xeb\xe7\xa4\xff\xb69\x9d\x12\x89]r\x11@!\x84\x86$\x03\xc9\xda\x0e\xab\x19 6\xc7~F\xdc)\x8e\x95F3Vh\xc5\x1a\xb1b\xa8\x94On\xfc\xce\xcd+\xd8\xe9U\x94\x168\xcf \xcb\xc0\xa7(z\x16\xb1L`\x92\x88\xaa5\x8c\x8eM\xe0\xce\x1f=\x8c\xef\xfe\xe0\x01\x1c8x\x04R\bh\xa3\xa1\x12\x858NP\x1f\x1f\u01cc\x9e*>x\xe3\xc7\xf0\v\xb7\xfc\x1e\x9as\u07ceC\xc3\x1a\x82\xb5\x85\x05\xb5\x82V1\x94\x8e\xa1B\x86\x98\x15\xa2\xb7?@o(\xac\x02\x9a\x19\xac\xadD_\xb5b\u8e05\xa4\xd9@\u04a8#\x99\x98@k|\x1c\xf1\xf8\x18T}\x1c\xaaY\x87\x8a\x9b\xd0q\fVy\x17n\xb4\xe5\xb2\x1bcla\xd7\x1aFi\x98D\xd9N\xde}h\xa5\xdc1r\u05c2\x01d@\xa8\xcc\x0eP\x9b\x1f!\x9a\x1d\x02\xa1\xc8!\x17\x81\x82\x01\x96\x95\u06a7\x17\x9d\xef\xf5\xc2\x05\b\xab\xfc\xbe\xe3\fb\t\x04\x10\xf5H\x88\x81\x10\xb2W\xe6\v\xfc$X)\ta\xe3\xf7\x84\xc8\xe1\x15\xe1\xcf6\x04r\xae\xbc{\xa1t`\xe2\u4ed6o\xceh\xc5u\xcc]z\x06\xe6-Y\x03\xad\x93\x82\xbf\x0e\x91\xc0\xe1#C\x18\x1b\x1b\xff\u022e]\xdb\x17\xd8\x19\xe0\x9d\xf4\xf3Z\xcc\xe5\xd1<\xf9\xb6\xdbn\xc7K/=/\xfe\xfe\xef\xbf\xc1\xb7\xde\xfa\x8f\xeb\xbf\xfa\xd5?\xdf\xd6l6o\u063f\u007f\xbfh4\x9a\f\"2\x8e_\xbdm\xdbv\x8c\x8eO\xe0\xfc\xf3\xcfE\u007f\u007f\x1ft\x12\x03\xccH\xed\x99S\x87\xba\xb4\x935>\u036e,\f\xe2\x18\xa0\x92\xff_Z\xf5\x927CS-\bS\x04y\xf04\x82\xd2\t\xc7\xfe\x1cn\x1bz\x02V\xf8UC\xe2\x86\u04e6\xb0(\x1d\xaf9\x9a1\x8c8\xd1h\u0116K\xae\x8d\x1bxj\x93+e\u0459\xf1\x99\xee\xb5\xdb\xf9\xd1\xfe\xf85\xb7(qL$!\x11Vj\u0631\xef \xee\xb8\xfb~\xfct\u0773\xa87\x1a\x10B Q\x89\xa5\x1e&\t\x92V\x8cU\u02d7\xe0\xc37\u007f\x1a\xa7]w3\xf6%K0>\x9c \x022X\x85a`\xc00! g\n\xd4z\tU\x18\xc0\xc9\xf8-d\x92v\xda1t\xabe!\x948\xceqo\xa5l\xc16\xd6\x05\xd0?\xb9\xd46%\xf1\xfd_\n,\x1dW\xf4\x84\xb4\xc6o\xe9,\x87\xdc\n&\"\x01\x8a\x84\x9d;\xb4l\x97O\xa9\xaa\xd2?\xd7\xee\xa4R\x99\u027d\xc7\xc7\xcf\u0198~(6\x01R\x12\u008a\x00\xe6D\bf\x87\b\x04\xd96\x8d&\xbf\x963\xa7R\xe3\\\x0f\xb9\xe8\x1a\u019eL\x97\n\x9d\\\xaa\xae5@\u02c0[\f\u048c0\xea\xc5\xc8\xe1\xbd\x18\u0739\xd1y\xe2Pv\xce\xc0\x06Q\x14.\u073dg\xf7\xb7\x9ez\xf2\xa9\x03\xdf\xfd\xee\xf7~n;\xf3\xe0h\u007f\xe0\x82\v.6\xeb\xd6=*\xae\xba\xea\x1a\xf3\xcew^\xfb\xad\u035b^o\xd5j\xd5[\xef\xbc\xeb\xee\u0783\x87\x0e\x19\x80\x85R6\xe0\xf6\xae\x1f\xfc\b$\x04\xbe\xf8\x9b\xbf\x82\xc5sf\xa0>6j\xbb)\x12`\x92\bX\xb7\t`\xc8R\x00\x8b\xd6\r\xedb\xbd\xa3\xc2~\x8f\xb7\bN\xf5\xbb\xd2 a\xff\x8by\xacW\xc1\x9e\xaf V!tr\xc6A\x1d\xf7S\xe9\x0e\xe5h\ni7\x1e~Y\ta\"\xc8\xcc{$\xdd\xd2\u04d4\u01fe\x9d>\u0775\x88\xb3\xed\xbe\x13e}s,Fn\xa9j\xda\xf0\xa4|\x98\xe2\x0e\xadS\xaa\xc8m\xef\xc00C:\xc8\xe3\u535b\xf0o\xf7>\x84\xd7\xde\u0612m\xd5ce\xdd5\x8d\xb1\xa6gW\\\xf1v\\w\xe3/\xc1\xac\xb8\f\xdbFfB\x0f7QqT70\xdb\x10b6@@\x88f\x00\x95\x9aB\xa04r\xe1g\x8aU;\xf8\xc0\x15\xaa4\x9e\xae}H\x9c\xa6!Y& e\x05\xb2\xa8\x9c\xf5\xf8\xfe\xc6\xd22\x89\xb5\x15\x10\xb1\x00sP\x84\xb9\U0008a360W\xa2**\x80\x00\xf4\x91\x04\x1csg\u01b7A!g3\xe3\x98\x17|_\xa835\x0e\x80\f\x04\u0090@\xb3\x02\xd0@\x88 @F)\xb6?\xc3]\x9b\x12\x9b8$ \x03\xeb\vo\x8c\xce\x17*a\xbfg\xb7[\x94\xef\xc0\xd8W\xde\x02H\f\xb4\xd2H\f\xc0\x94`\xe9\ua2f1\xe9\xc5\xfbph\xef\x16\x84Q\xd5\xd5\x0f\x01\xa5\x15\xf6\xee\u06c7\xbe\u07be\xcf\x02\xf8\xe2\xcf3\xcc\x12\x1c\xcb\x0f]u\xd55\xe6\x81\a~\"\xae\xbb\xee=f\xf5\x9a3\xbe\xff\u66dbxdx\xf4\xd6\x1f\xddw_\u07e81LD\x94$\t\xc0\x8c;\xef\xbc\x17:\x89\xf1{\xbf\xf5+8u\xf1|\x8c\x8f\x8eBi\xe3\x06E\xe4.t\x82\xc86\xd3%\xfa\xc4\x13\xd8rwS=\x1eMa/\n\x88r\xbc\x9c=\f\xc1\xff\x1c\x9e\xe5g7\x1b$\u007f\xc03\xd9`\xf4X\xf8U\x93\xcb\xf49\xf3\u02a1\xb6R\x99)t\xb9\xfb\xb1\xe2i.\xa6\u06b0\r\\N\x95\x9d\xcaX\xba\xa1\xc9\u04c1\xdam\x1b\n\xdcv\xcfG\x9e2\xbadn!L\x9e\xaa\x98\x19\bC\x89@\x06x\xf2\x85\x9f\xe1\xbbw?\x84\x9d\xbb\xf6@Jk\xbf\x1a+;TTI\x82(\fp\xf5UW\xe2\u068f\u0708#\x8b/\xc4\xd6\xe1*\u0091\x16*\xdad;G\x8b\xef\x02\x88\x80\xa0_ \xecaH\x9d\xc0$\xbeN\x92\xb3\xf3\xc7\xe9\xcfx\xe2\xa6\xe2\xf1\xe2\xdc\u007f\xa4M\xb0S\xb0,\u02c4c\x9c\x15Hr\x8b\x06\x1b\xdb\u0676\ufb6dw\x8c}\x05Y\x13\xa8\u032f\xc0\x04\x02\xeaP\f\u0772\xb1z\xd4\x01\x99g\xb9\x9d\x00\x00 \x00IDAT\u0561\xcde\x8d\v\x17\x1f\u00df\xd83d@\b\x02\x82\xe8\x91\x10s\"\u020a\x00\x99\xa2\x81\\g!\xf7--\x9c\x96CJ\xabh\u054e+C\x9c\rk\x99\xa8p\xbc\bN0\x06\x02\fC\u0156\xc2j\x1b$\x8d\x81E+\xd1?\xb0\x04\x87\xf6lvL\xa1\xdc\x1a\xa0>\xd1\xc0\xc1\x83\a\xdf\xcd\xccU\"j\x1e8\xb0\a\v\x16,9y1\xf3\xf6\xc7u\u05fd\u01e4\xf8\u04eaUkn\xbb\xfe}\xef\xf9\xb5\xcb/\xbb\f\xfd3fP\x14\x85\x1c\x85Qv\x83\xdd}\xef\xfd\xf8\xef\u007f\xf2Wx\xfc\xb9\r\x88\xaaU\xf4\xd4*\x8e\xb5b\xe9n\x86rq\x05\xa1\x18\x12\x9c}\xdd\x13\xa6\xd1\t(\xe8e]*\x97\f\x1a\xdb?LF\u0663\u008e\xc2d\xd1\xc5\xc5!g\xa1\"S\x113o\x87\xbc\xa9=\n\xac\xa3H\xf21\x17r`\n\xf9\a\xb3g\x00V\xb2s\x98\xe45'\x9bg\xa4B\xa0fK\xa1\xd92h\xc6\x06J[\\<\vW\xce\f\br\xd6\au\x8c\u073c\u063e<E\xad\xe0:\x98\x0e\xa0\xab\x95\x10\x81\x94x\xec\x99\xf5\xf8\u07f7\xfd\b\xdbw\xee\x86p\xdd^\x92X\x98#\x89c\xf4\xf5\xf6\xe2C7\xbc\x0f\xd7\u007f\xf2\x16\x1c\\p\x016\x8dU \xea\n\x91\xd6)\xa5\xdb\x05ZXH\x84\x04CT\ud015\x95\xce1m\xf7\u007f\xa4\xec\x13\xe73\xa23\x9e\xb8W\xac9\x0f\xd3((\xfa\xb9\x9d\x86\xea\xd9C\xf8\x17J\x1b\x90]\u0282\xca\n5\x10V\x05\u00b9!*\xf3\"D5\t!QN\xdd\x12(\x1a\x03!\xa7\xfa\x16\xba?I\b%AT\x04\u011c\x10\xb27p\xb5\xdf\xebV\n\xd7>\xb9B\xdef\x95\xcb6\x81I\x04\x01HH\xbb\xdb\xc8\xdc+\xc9\xfe\x1bT\\W\xd86}F\x19$\xb1\x816\xe9\xa2n \xa2\bs\x97\xacAT\xed\xb5\xe7\xcb->\x82$\x12\xa5\xb1c\u01ce\xd5/\xbf\xfc\xdc\"\x00?\x97\x85\xfc\xa81\xf3\xf6\xc7'?\xf9\t\x9cw\xdey\xf4\xe0\x83\x0f\xe1\xb6\xdb\xfem\xe3\xef\xfc\xce\x17\x16\f\r\r_\xb2\xff\xc0~\xeb\a'\x88\x8c\x1b\\\xec\u0735\a\u03fd\xf8\n\xa4\x108u\xd9R\xf4\xf7\xcf@\xa2\x8d-\x8c\f\xebW\xec\xd6\x16Q\xd8bR\xe6'\x91]s\xe2\xc4LC\xb9C\xe5V\xd6\x05\xe7\xfc\x94n\xae#^)j+?m\xc0\x01\xb5\xd5\xf5\x92\xd0\xf3n\x90E\x19\x0419\x0e>\x05V\x8f\x12\x96\r&g\xc5\xf0tw\b\xee\x05\x94\xb6\xd8x3\xd1V\xd5\xe9\xe4\xf9:\xf3W!\xcf\x12*\xff\x1c(\b\x11\xbb,b\xe4\xf9k;j\xa8 \xd4j\x154[1\x1eX\xf7,n\xbb\xe7\xa7\xd8?x\x18a\x10\x80\rCi\r\xa3\r\x1a\x8d&\xe6\x0e\xcc\xc6\xc7o\xfc\x10.\xb9\xfe\xa3\xd8Z[\x89\xad\xcd\x1a\x82\x98QmiH\xe5e4\xa4\xb9\xa0\xc4@\x00\x04\x11l\x82|\xa1\x9b\xf6\xbc*\t\x85\xecQ\x12T`\xc1\b\x19 \x8c\xaa\b\xabU\x04a\x04\x19F\x96\x1dS\x18(S\xc7\xf5\x05\xe1\x162a}\x83D !\x82\x10\xe4\xaa3y'\xc5\x17\xf8\b\x06D@\xa0\xaa-\x98B\xc3v\xd1\xde.\x87\xfc\v\x92\xca\aA$l7\x1e\x04\x02\"\"\xc89!\x829\x91\vW\xf6\xad\b\xbc\x06\xbc\xf4J\xf1\x96\xea,\xe5\u02e4\xc9!E]\x95?\xe8N\xcf\x03\x80V]C7\x8c\xfd\xdb\\\ve\xed\x174v\xbd\xf1,\x9a\x8dQ\u0220\x92\x89\x88\xb4V`c\xc4\xf2\xe5\u02df\xbd\xfb\xee{6\xfc\xc7\x00\xb4\xe4\xf1\x9d\xef|\x17\xd7_\xff^\xdc\u007f\xff\x03\x00\x80{\xee\xb9\xf7\u01fbw\xef8\xad\xd1h\x9es\xe8\xd0!J/n\xe3\x9c\xe6FG\xc7\xf0\xcc\v\x1bp`\xf0\b\x16\u031f\x83\xc5\v\xe6B\b\x91\x19)\x19\x88\xccMQ\x90_j\u06f8\xcd\x1e'\xfdXg\x95\u9ad9l\xfe\u07aet\xa4\x82\xea\u0447W|\x9b0\xf2\x1c@\xba{\xb1{e\x8a&\xe7\xb4w\xe88\xa6\xb9\xbb8\xdeY\xc0t\x06\xa8\xd3}h\xc3H\xb4\xb1~\xf2\x0e\x1b\xb7B\xa04\x00\xa4\u0762\x81\vXr\x0e\xd9z\xdb|\xcf\x11\u0424\xb0K\xba \b\x81 \bP\xadD\u0635\xef \xfe\xed\x87\x0f\xe3\xc7\x0f?\x8d\x91\xd1q\b\x17.\x92R\x0f\x9b\xcd&\x96.\x9c\x87O\xdc\xfcK8\xe3\x1d\x1f\xc0\xab\xb4\x14{\x92*\xa4aT\x9a\x1aA\x92\x17\x12\xf2\xa8\x91l)<\b\x88!B\x80d\xfe\x9e\n\xe7/\x1d\xbe\x91\xb0\"\x19ae\xecA\x18BV\xaa0I\x82\x03\x9b7`\xcb\x13\xf7c\ubccf\xe2\xe0\xd67 \x84D\xcf\xcc\x01\x17f\xe1\x0f3\xbdE\x9e,\xbf\u074aml!\x97A\xe0\f\xe1<\x0e\xbe\xa7\xbb\xf1wz\x90\x04\xaaX\x93.\xa1\x1c^\x9e\x0e5\x99\x8ab8\xa2B\x87.$\x10\x84\xc2\x05-\x13\xc4\xec\x10r~\x04\n(\x17\x06Q\xb7\x96\xa1\xfb\xfe\x90<og6\xa6\xc3\x054\v\xd8\xd66\xf8Ci\x83FK\xc344(\xb1\xb3\t\xc1v\b*E\x88\xa8Z\xc3\xf6W\x9f\xc0\xf8\xd0\x01\x04\x0e\x19 AH\x94B%\nq\xca\u04a5\xad\xc7\u05ad\xbb\x03\x00\x1e\u007fb\x1dn\xbd\xf5\x1f\xff\x033\xf7\x1f\xbf\xf7{_\xcaC\x8c\x8943\xffZ\x18F\xa8\xd5j\x9f|\xfe\xf9\xe7\xc1c\xe3\xa0\n\xa1\u0572\xc1\f\xcdf\x13w\xdc\xf3\x13l\u067a\x1d\x9f\xfa\xd8\xfb\xf0\x9e_\xb8\x123f\xf4\xa11Q\xb7\xb44\x10b\x04\b\u0666JJX\xf9rf\xa2\xe2\x17\xf1\x94\x9b~\f\x86S\xbe;E\xea\f\x93 @\x02\x89\xc0\xc9e\u049e\xdc\u03ef\xcc\x13\xae\x18y\x881w\xc0\x12T\xc2\x12\xc9\xddh\xca;\\\xc6$\x98\xfa4\x8b/\x9f\xa0\x01\xef\xb1\x14q\xc3\f\xe5(\x86\x896\u06403\x93\xe2\x1b/&\x82\xbcA\x1fS\xc6\x15G\xc1\b\n\xb9!\x13\xc1\xfb~>\xf8\f\x02\x89(\n\xa1\x94\xc2\xd3/\xbd\x8a{\x1f|\x12\xafm\xden}\u0209\xa0\x13e\xe9\x87J#n\xb5\xb0r\xd9R\xfc\xe2\xa7n\u0092\xb5\xef\xc1\xcb\xf1\\\x1cIB\x04\xc2 L\fd\xe2\x16\x88\x94\x1dbl\x04\x0fI\x17\x15(\x81\x80%\x02- \xc2 \xf7\x01/`\u0294\xc1G\xa9\xc531P\x1f>\x82]\xaf<\x8b\x9d/=\x81\xc17_\xc5\xf8\xa1\xfdPI\x82JO\x1f\xf6\x9ey>.\xb8\xe1\xd3Xx\xfa\xb9\x801\xd9\f\x83\xdb|\xe13\x1e\xb6\x83%\v\x1eZ\x93\f\x1e\x01\x17\x06\x1d\x10\u012c\xd0r\xf1\x87\x12P\xc3\xc2]>|\xc5\xf6D\xe6l \xb2\x85<\f\\JP_\x00\x9a\x17\x82B\xe1<T\xba\x14r/0\x8cPf\u06563[D\x10\xc0h\r2\xc6QI\x1d\x1f]\b04\f\xacG}#6\xd0-\x8dP1\xda\x13\xbb\xb5\xd2\b\xc3>\xcc]\xb4\x1a\x83\xbb^\x831\x1a\"\xc8\u0565q\x92`\u04d6\xcd\x17\xa6\xbf\xfb\xca+\xae\xfa\x8f\x01h\xe9\x85\xe2,5\x17/^@D\xd4`\xe6\xdf\xe8\xef\x9f1X\xadV\xbf\xb8a\xc3\xcfp\xe0\xc0~03\xc7IB\xa9\xb8\u854do`\xcf\xde\x03x\xed\xcd\x1d\xf8\u055b?\x86\x95K\x17\"N\x12\xa8$\xb1\x1d\x1d\vH6\b\xa1\x10\xb0C\xa9\xa9\x8bS\"\x15g/G\xa7\xfcd'V\"w@4\x82\f\x15/w;\x14\x05.H\xb1|\xa6\xaf\xa3=\x01t*d)\xf3\xf6\xe7\xe3(\x9e'\xa2\x90\xd3q\x16\xf0\x94q\x94\xe6s*\u05c5\xa7TC\u035c\x8bC\u041ejFY\xdef\x16\x15\xe4\vm\xbcX7\xf6\xd8\x1a\xe4\xb6\xfd\xd5J\x15a \xb1{\xffA<\xf8\xf8\xf3x\xf8\u0257p\xe8\xc8\b\x84 \x186P\x89\u016e\x95\xd2\xd0*\xc6Y\xa7\xaf\xc0G~\xf1&\xcc=\xf7j\xbc\u051c\x8b!\x15 $\x03\xd2\f\xd1\xd46\x04\x81$d\xe4\xe0\x8bD\x83\xb5\x82\x86\x82Jb\x98\xa6\x027\x19aS@$\x01\xa8\" D`c\xcd\x18PI\x8c\xe6\xd8\b&\x0e\x0f\xa21z\x18I\xa3\x0e\x93\xc4\x18\x1d\u070b}o\xac\xc7\xd0\xeemh\x8d\x8dX\xf5fda\x16\xad\x12\xec}\xf5E\xcc^\xba\x02\xb3\x97\x9d\x86\xb0R\xb5\xf8\xbb\u020b\xac\xf0\xf8\xd7$\x85\x85K\xb2,Z\xce\x06\x9fS^\x1bi\x82\xd1\xcc\xc0\xed<\x14$3\xa0\xbdE6\ud4055\xf4\xaa\x046\\\x19\x82\x80\x1eKCDE\xe4\ucb2eq'\xf0\x98F\xdc\xe5\x9at\u0443BB\x04\x12l\xa4m\f\xd9X\xdfsw\\\x8d\x01Z\x9a\xd1L\f\xa2\x84!\r{a\x16\xee}$\n$\x02,8\xe5llz\xe9'\xd0*\x81\x91\x01\x04\bA\x10bl\xbc\x8e]\xbbv/x\xe1\xc5\u7bbb\xe8\xc2K\x1e\xf8\x0f6\xcb\x14\x05\xfd\u99df\xc4\u06b5\x97\x83\x88\xc6\x00\xfc\xce\x13O\xac{\xfa\xae\xbb~\xf0\u01cf=\xf6\u061am\u06f6\xd3\xe8\u0628VJ\t\xa54\x81\x19\x87\x87\x86\xf1O\xffz\a\xf6\xef?\x88\xcf\xfc\xd2\r8e\xf1\x02\xf4\xd5\"\xf4\xf7\xf7C1\xa1^o 1\x12\xa1\x90\b\x9chEd\xae\x8a\xdc\xe9}\\t\x01\x9a\xd4\u00ef\xddN\x97\x00DPmE\x9a\xba\xbe\xceT_K\x87\xa2>\xf7\xc5\xef,i\x1a\x05\xf2h\xa0\x93N\xaa!\x1f\xd7b0mHE[HE)\xceB$\xb4\xb6\x18\xa7f.\xaa\\\x19\x1dA\x1d\xa9\xbc\x9d<\u0715\xbc\x00\x8aB2\x8d\u00e1\x830@%\n161\x81\x176l\xc2=\x0f<\x81\u05f6\xec\x80R\x1a\x950\xb0\xbf\u07e4\u0656\x84\x9e\x9e\x1a\xce9\xfbB\xbc\xf7\xa3\x1fEe\xf5Z\xac\xaf\xcf\xc0\xa8\x91\b\xa5\x01\x91@\x90\x00\x91\b!\x03\x03\xd5j\xa01v\x18qs\x1c\xcd\xd1\u00d8\x18:\x80\x89\xe1\xfd\x98\x18\u068f\xb81\x0e\u05b1\x85*\"\x89\xa0VEP\xadA\b\x89\xa4\xd5Bsl\b\x8d\x91!\xc4\xf51$-+\b2*\xb1>+I\vB\x06\x90a\xe82?M\x16\x1bW\x1f>\x8c\xc3;\u07c4j5\x11\xd5zm\x97\x9aA6\xb6C\xb5B\x1b\x99\xbfF\x10\xa2`\x14=\xcd\u055b\x00\x88P\x00\xb3C@\x10\xe4\b\x01\xb1\x014\xa0\x13\x83\xc4\x15_)\b\x95\x80\x10Fv\xf1@E\x80g\x05@U\x80\x8d\v\x9dqP(qq\xb8\xef\x13\x00\xda=8\x19E\xd6V\x16\xb0.\x03\x18\xa9AZ[k\xect7G\x84D\x03\xf5\x96\x86I\f(6Y\x8a\x14\f@\x9a\x9dy\x9a\x86\x90U,Xr&z\xfa\x060:\xb4/\xfbE6\xec9\xc1\xf0\xf0\xe8\xec\x87\x1e|\xe8b\x00\xffQ\u0327z\xac]{9\xa7\x90\v\x00\\q\xc5U\xdfc\xe6\x9f\xfc\u065f\xfd\xd9o>\xf1\xc4\xe3_\u06bcy\u04ec]\xbb\xf7\xa2\xd5j\xb1J\x12JO\xe4\x8f\x1fZ\x87\x177\xbc\x8a\xd3V,\xc3\xcae\x8b\xf1\xb6\xd3O\xc3\x05\u7781\xd3O[\x0e&\x89\xb1\x89:\x9aZC\x92\x84dv>\xe9\xd6\xf6\x899\x87.\x98\x8f\x06\x05\xee\xe2nX\xb2%\u0325\xc9\xdc\x01\x9f\xf8\xd1\x1aT\xd2\xf5\x17Cp\xbb\x8f\x0e\xdb\xe1\x96\xf6\xa7uc\xa2\xd0q\x94d\x9a\xe6B\u046d\xf0+m\xe9a6\x9b\xb3]\x8a\xdf)\x1fL-\x00\f\xa7v\r\x94\xb3=\x88\x8b\x8e\x87(H\x80\x00\x00Q\x18 \x8aB\x8c\xd5\x1bxu\xe3\x0e<\xb8\xee9<\xf1\xfc\x06\f\x8f\x8c#\f$\x82@Bi\xe3\x15\x16F$\x03,;u\x19.\xba\xe6]\x98\x98\xb3\x06/\xef\xd7ha\x02\xa1\xb4\xe97Fk\xa8\x91\t4\x8e\x8c\xa21<\x88\xe1}\xdbpx\xcf\xeb\x988\xb2\x17qs\fI\xab\x01\x1d7]\xb6g\xee\xfc\x92\x05-\xa4CP\xa3-\x17\u06a7\xf39\xae9\tB\xa5\xa77\x17\x1f\xa52}\xad\x11\xd7\xc7\x11\xf5\xf4a\xf1\x19\xe7\xa3\xda7#\x17\xbdd3I\xab\xd4$\x87\x93\xa7CSHYlb&=Y\xd4I1\n\bbf`\v\uaa06hh\u02002\xbb\xe1\x00\xb0\t?\x86\x81\x8a\x80\x98\x19\x80{\x84\x9bW\xf8\xd7iz\x8fP\x9b\xf6\"]\x9c\xfd\xa6\xc8\u3d34[\x05\basS\xc9*\u008d\a\u02754#\xd1@\xa8l\xf1\xce\xe6\x02i4`F\xff4\xe8\x9d1\a\x03\vWal\xf8@\xfeur\r\x03\t\xec\u077b\xef\x1d\x00\xfe\x14\x00\x9e|\xf2q\\~\xf9\x95?7\xc5\xfc-\x13M\xfe\xe3?~\x93~\xe5W~5\xbb\x95\x1fz\xe8\xfew\xdf}\xf7\xbd\u007f\xf2\xf4\xd3O\xbf}\u02d67111\x818I`t\xb9\x99\xd9Y\xa7\xaf\xc4\a\xdes\r.\xbd\xe8<\xbcm\xcdJT\xab\x15\x8c7Z\x88\x95-\xe6\x15$n\\\n\x87zs\xdb\x14\xfd\xc4<\xba\xb1(\xb8\xf0yqPk\xda\xfa\xe2\xb43\x17\x05^\xcc4@}/v\xfd\xe8f\x01\u04c7X\x8eZh\u0140r\xcc\x10\xad<{Z\xe7p\x98\xfa\xa9\x982\x17\xc9\xff\x8f\xbd7\r\xb2\xe4:\xaf\xc4\xcew\xef\xcd\u0337\xd5^\xd5\xfb\n\x10\r\x80X\tb\xe3Np\x81(B\x94DK3\x1cihY\xd684\xe3P\xcc(\x183\xa6\x15\xe1\t\u02e3\x88\xb14\x94\x1c\x1e\x85\xc2\x16I{,\xc7\f\x15Z\fQ\x96\xc4m8\xdcA\x90\x00\xb1/\xc4\xd6@7\xba\xbb\x1a\xbd\xd6\xfa\xf6\x97y\xef\xe7\x1f\xf7f\xe6\xcd|\xf9\xaa\x01nSptF\x14\xd0\xdd\xf5\xeaU\xe6{\xf9\xce\xfd\xee\xf9\xcew\x0e{\xbe;\xec\xf1\xe1\xa9\xee\x98\xf3+M\xfd\u0749\b\x81\xab\xc4/\xaem\xe2\xd9\x17O\xe2\u0467\x9f\u01c3\x8f=\x83\xd3g.\xd8\u079ep\u0563\xbfj\xc0r\xf4J\n\xcc\xce\u0361\xb9\xb4\a\xa3\xda,X\xd5\x10F\x01\x94\x8a\xec4\xe7h\x04\xbd\xb9\x81\xde\xca9\xf4\xdb\x17\x10\x0f{\xcez\x16\xde|@\xb1kM$\n\x00U\x90(ybs.v\xf3\n/z\x12[/\x96\x1dW\xbe\x11o\xfa\xb9\xff\x12ox\xeb\xddPa\xe4\x85<\xbb\xa6\xa7\x14\x10A\x00\x15FPadm3\x84\u020a\x18P\x15oWV\xb5\xe7Z}\u00dc\u075f\xd6\xdf\xc4\x00\xe7G\xa0N\x02c\x80\x81{OC\x82\x95 \x06\x04\x9a\r@\x8b\x01\x8c\xb4 \xcdd\xfd`R\n_PQ\x1b\x9fs\xe5<\xa6h\xc9*\xf3\n\x9fw\xa3\x13\xc4\xfd>\xe2Q\xec\xb2U\r\xfa\xfd\x18\xeb\xed>\x92~\x8c\xa8\x1bC%\x06R\x00\x01\x03\xd0\fh\xeb\xf1\x04\x86\xf5e!\xc2c\xdf\xfaS<\xf2\xf5\xff\xdb6\xa0\x9dm\x80N\x124\xeb\x11n\xb9\xf9\xfa\xa7?\xff\xf9\u03fd\x87\x88.\\\xae\xcc_\xe5q\xe3\x8d7\xf2'>\xf1\t|\xfc\xe3\x1f\a\x00\xbc\xf7\xbdw\u007f\x99\x99\x1f\xfaW\xff\xea\xb7\xff\xf1\x13O<\xf9?>\xf3\xcc3\xf5W^9\x83~\xbf\x8f$I\xc6~\xfe\x99\xe7\x8f\xe1\x99\xe7\x8f\xe1\xe0\xbe\xddx\xe7[\u078c\xf7\xbe\xebN\xdcz\xf3u\x98m\xd6m3U\v\xd8E\u06a0\x18%Q\xb2\xea|\x95\x15ieu\xea\u0758E\xba\xa3z\u0528l\xc0UUR\xb3\x9f\xc4\x0e\x14$e\x93\x90\xb6*\xddh\xf2>\xa2z\xff\xf0\x83\xae\xe2)\x96\x19cUI\xb1\xb1\x94J\x9e\xfaS\xde\x15\xe5\xd5P\nt\xe9LA*\xee\x14p\xd9S\xe9\xf5\trS\x90\xec\xf9WK\x04JA\t\x81\xf3+kx\xfa\xe8q|\xef\xf1g\xf1\u0533\xc7pqu\xc3\u01baI\x91\x9f\xa5\x1f>\xec\xe8\x1d\"@\x1b\x83\v\x17.\xe0\uc673\x16tDn\x86\x95\xf3Y\xe9X\xbc\xafe\xa7|]\xa0\xf1\xb6t\xae\xc4\xf0\x16\x0f6\u067f1\xe7u({\x1aq\xa3\rt<\x04\x18\xb8\xf6\xae\x0f\u1dbf\xff\x8f\xb1\xf3\xaa\xeb0\xec\xf5`t\xe2\x940\xf6<\x85R\x10A\b\x15\xd6 \xc3\u0426\xe9\x00\x85\xa1\x98R:u\xc5\xddIYsZ\xfb*\"\x01 \xb6\u065aF3X\x10\x928o\x88j\x00\xd2]\x9b\x88\rL[\x83[\xd2:\xade}\u07f42v\xaa\x17b\bL\x8a*\xc9\xefI\xa2\xea\x1b\xdd\xf7/O\xafch\x18\x9a\tR3\x843\x0fK\xef=2\u0206\xaa\x88\x81$\x89Q\xab\xb7\xb0\xb0\xebJ\xc80\x82\x8ec\xa4\x01DRH\xf4z=\xac\xae\xaf\xed\xfa\xd2\x17?\u007f+\x80/^\x06\xf3Wy\xbc\xf9\u0377e\u007f>}\xfa$\xfd\xcb\u007f\xf9\xdbDDk\x00~\xef\xcb_\xfe\u04a3_\xfa\xd2\u007f\xfc\u0503\x0f>x\xe8\xc9'\x9fB\xbb\xdd\x1e\xc3\u04f4\xa9zb\xf9\f\xfe\xc3\xff\xf39|\xe9k\xf7\xe3\x9doy3>\xfc3\xef\u016d7_\x8fz\xad\x05\x13\x0f\xc0\xc9(\xe3\x18\xdd~\xad0\xfcR\x05\xdaU\x80\xe6\x9b\u0231\xa7\xbc\xc8e^T\x01\x9d\x19\xc1\xe3Et\xf9|~9\x93\x87\xe1\xe7\u07fd:@\xe5B\xd5\u03d7`\u0479:\x93h\xe2b\xb6\x15o\x0f\xd8\x0fN\xa2\xd9\xd2)n\xd8'\xd5P\xe7\xa6W>WJn\xea\x913\x8b\x00O\xba\xe0\xc9\xf9(\v\xa7'\xa4@.\x10H\t\xa9$\x8ca,\x9f9\x8f\x87\x9fx\x0eO<\xfb\x12^<\xb1\x8c\x15\xd7\xdc\x14\x04h\u0599\xd5+\t\xdfm\xdbQ7^\xfe\x9e\x90.\xd75G\xe1\u0716\x96\xb7&\x99R|\xce\x1f\xe2\xe0*\x05{\x81\x82\x97}6z\xee\x82[\u04a1\xa3\xf4\xcb\x18\x83Zk\no|\xef\x87q\xe7/\xff\x06\x9a\x8b;1\xe8lzT\x83\x93\x1f\x06!d\x18Y\x10\x972\xcb\xed\u012b\\\xa2\x99\xd2\xe0t\xbbK\xd2\xc6s\x97\x14\f3b`-\x01w\x12g;\xec\xde\v\a\xa4\x9a\x80>\x01\x92\x19r` \xe2\xd8V\xc2S\n\xc25@\xb3\r\x87\u06cd\x18\xa6\xcc{\x89\xb2\xa6lu\xd1A\x15\xea\x17\xc0\x99\u007f\xb9k\x1d&\x8c\xa1\xf3\x99W\xda)r\xd2\xdfk\xac\xda\u021ff5l\xef\xcf\xd6\xccN\xb4fvb\xfd\xe2\xc9\xcc7\x9e\x04!\x1eiN\x12\xbd\xf8\xf0\xa3\x8f\xdc\n\xe0\x8b\x9d\xce:\xb5Z\xb3|\x19\xcc_\u00f1w\xef\x01\x86\xcd\x14\x95w\xdf\xfd\x01}\xf7\xdd\x1f\xf823\xdf\xfe;\xbf\xf3?\xfd\xdbVk\xea\x97\x1f{\xec1\x9c?\u007f\xbeP\xeaf\x8d\x11a\xb7v\x17V\xd6\xf0W\x9f\xfb\n\x1ex\xe4)\xfc\xd4{\u078a{\xde\xf7v\\s\xd5!\xcc4\x1aP\xd0H\xe2\x18q\x1c\xdb\u02a2\x94\x95I|\u9294\\\xf52r\x9e\u06a9\x97v\x9ar\"\x05Y\xf5@aK\xe9\u03f9S\u05bc\xf3\x15\x18\x94i\xa3\xd9Q-T\x92t\xf0\xe4\x91\xd4Kp\xecE.\x1e\x85\xdd\u026b\xa9\xc6'%-1[\xad\xb8\u05b9\xccP\xeb\xbc\n\u03f7\xec\xe3K\xce\x18h\xbb\xd2^P\x1e\x1dB\u0796\x842><@\x10(\f\x86#\x1c;\xb6\x8c\xef<\xf24\x9e\xf8\xfeQ\x9cx\xe5,\xba\xdd~\x0e\xe2I\x82D\ub8bf\xb9\x11\x05\xc4\xf5\xdb\xd0i)\xca^\xf6l\x1a\x98\x9cJ\xfc\xf2\n3\x05B*\xc4\"g3\xaa~@\xab%\xc5\xf3\x85_k\x18\x13\xc3$\xb1\xa5J`w\x17\xb6i\x19@\xd6\xea\b\xeaM,]\xf1F\\\xf3\x9e\x0f\xe1\xd0-oG\u051c\u00b0\xb3\xe9\x02\xc4\xedh\xbb\x90\xca\xd1*5\b\xa5<\x83\xa2\x92-\x00\x97\x03\x1cx\x8c\x0e3\u0657U\x17%\x06\x88\x8d]\x98i3\x81\xe8h\v\xd0\xc6\x01%\x91}I\x8c\xb5j\xd1\x00\xe2\u0600b\xb7\xe0\x8e\fxh\x80\xf9\x00A \x10\xc0R,\x82r\xd2)\xad\xfe%\xa5!\x1d\xbc\xe5}\xc7^\xd4\"\x93\xe5\xcdA\x02\x9a\r\x06\t\u00f0\x05r\x99x\xbd\x14\xc3 \xe3\xf9\xb6\xe7\xac\x17\xb4\x8eQk\xcc`a\xe7a\xac_8Q\xbc\xaf\rF\xbdn7z\xf9\xf8\x89\xeb\x00\xa0\u055a-\xf4\xf8.\x83\xf9k8\xee\xbe\xfb\x03\xfa\xbb\u07fd\x9f\xde\U00096df1\xe3\xac\xfe\xe1\xe7?\xffw\xf7\xdd{\xef_\xfd\x0f_\xff\xfa7\xf6-//S\x92$\f \x91R\x06\xc6\xe4\u06fe\xac\xca?s\x0e\xff\u05df\xfe5\xbe\xfe\xed\xef\xe1\xe6\x1b\xae\xc5\xfb\xde\xf5\x16\xdct\xf5\x01,M\xd5\xd1j\xd4 \xa5@\x1c'\x8e\x8fw\xa6Q\x82\xbcq\xf0q\x0e8\x05\xf2\xcc\xfc\u0254\x12\xe0\xe19\x1c\n\xca\x00*\xad\xd8\r\x17\xedk\xa9d\xfa\x9fZ\xfd\xda\xf1\xe2\x1c\xf0S\xb9b>\x04UE\x10\xf1\x96\x11p\x97\x8c\x87\u02ea\u03ca\xb0\vWys\xf6\xe1\xe7\f\xc8\x13\xe34\xe2\x06^\xcc\x172\x10-\x0fx\x8c)\xd2\xc0\xce\x10*\xd7\x031\xfb\xe7\x04h\xb6UW\x14\x86H\x92\x04/\x1e}\x19_\xfd\u03a3x\xe4\xc9\xe7q\xe6\xdc\x05\f\x86C+\x8bc\x83\xd10\x81N\x9b\x9b$2\x8d\xb2T\xcaM\x0fR\x9e)Ib\x82\x1c\xa88\x11@.\x8f\x16\xe5k\x01g\v2\x15\xc6b\u0269P,`\xb3\xb1v\xb5iU-\x82\b\xd1\xcc4\xc2\xe6\f\xa2\x99\x05D\xd3s\x88\x9aS\x98\x9a[\xc0\xec\xee\xfdX8t5\xe6\xf6]\x81\xe6\xec\x02\x88\xadU\xae\b#\x90T\x80\xfb\x12nQ\x13\x82<>~<\x9d\xb5j\xbf\xc9^\v\x82a\x81{\xa4\x19\xa3\x84m\x13\x91\xad\u03f9\xeaj\x04\x1d\x9d\xb9)J\r\x906\x85\xf4:2VVn\x90F\xf4\xb8\x02G\xc7\xd0\xcc\xe8N\xdbs\f\b\b%\xd91\u007fApsD\u064e\xc06\x8bKq\x90^hz\x96\xb1\x9b\xaa_\xa4\x02d\x82x\x94`\xe8V#\x19[\xa5\xa6I\xcd\u0334\xef\x83\u3337\u049d\xa4\x8eQo\xce`i\xf7\x11\x1c\xfb\xfe7=\xd5\x14\x10\x06R\x9d>\xfd\n\xd6\xd77\xaeg\xe6\xeb\x88\xe8\xfb\x0f<\xf0\x1d\xe1.\xf32\x98\xbf\xd6\xe3-oy\x1b\xff\xe1\x1f\xfe\xaf\xf8\xcd\xdf\xfc\x18\x01\xe0{\xee\xf9\xd0'\x99\xf9O\u007f\xeb\xb7>\xfe\xb1\xaf}\xed\xeb\x1f=}\xfa\xf4U\xe7\xcf_\b\x1c\xa8kr\xdd&\xe6bK\xfe\xf8\x89\xd38y\xfa,\xbe\xf5\x9dGp\xf8\xc0\x1e\xbc\xf9\xfa#\xb8\xe5\xfa+q\xc5\xfe\xdd\u06390\x87\xd9\xe9&\xea\xad\x10Z\x1b\f\x86\xb1\xe3\xe5\xd3j\xae\x18Ll\x00\x9b\x06\x9f\u4cba\x8cB\xf0@/\x05\xea\\vE\x13Io\xf6V\x8e\xbc\x92/\xf2\xeb\xc2q\xc4R\x90\xb5\x0f\x15y|\x96\xc8\x02}\xe9\x92\x16\xb9\x97\xecX\x96\xcf0\xab\xd6,\xff\xad\xbd\xf1\xf3\xb4\x92\xcb\r\xb0\u0704\x1e\xa8\x04\x1b\x18k&\xa4\u065a)\xd02\x8a\xf9\x9c\xe9K\x92\xaa9\xeaQ\b\"\x81S\xaf\x9c\u00f7\xbe\xf7\x04\xee\xfb\u07938\xb9|\x16q\x1c\x83\x88!\x05C'\xf6\xbd3F\x17\x16\xf6\x99f\x1d,\x02\f\x13\r\b\xe5Y\xa9::\x82\xca\xd1\xd4\xee}O+u\xe1\xde\xff\xf2\xf6\x82<\xbb]r`\xa15\x92d\b\x9d\xc460\xb8>\x8d\xc6\xd4\x02\x82Z\v\xaa\u0442l4\x10\xcd.\xa2\xb1\xb8\x1bS{\x0ecz\u07d5\xa8\xcf\uf08cj\x10B\"\f\x15\x1aQ\x80Z\x18@\bB\x92\xc4\xf6\xfd\xaf\xd5\x1d\x85\"\xa1\x85Mv\"a\x13\x84\xc0\xe3\xadl\x86\xbf\xeb\xc9{/\xc6\x03q\xe3\x16\xc9X\x03\x83\u0120\x9f\u062a<\x9d\xef\t{\x1aa\xcf\x05\x002 \x13\x06\xa5#\xfe\xeey9\r I\xe1Rx\x8e\x8e\x9a!:\x1a,\bqCbD@?a(\x01\x04\x82\x10)\x81@\x00J\x10\xa4p\xe7lJ\n-\"+/vU\xbd\u007f\xee ;\xa0\xdfO\xec\xdfe\u0090\t\x17\xed\x1e\xb8L\xdb\xe4K\xb4\xd1\tT\xd0\xc2\xcc\xc2>\x04a\x03:\x199+\x04{K\x8c\x86C\xac\xac\xac\xcc}\xfd\xeb_Y\x04\x80\x87\x1ez\xe82g\xfe\xc3\x1c\xbf\xf9\x9b\x1f\u00d7\xbe\xf4y\xfe\xdd\xdf\xfd=\xfa\xe67\xefc\xa7K\xff\x1df\xfe\xc3\u007f\xf6\xcf\xfe\xe9o>\xf2\xc8#\xbf\xb0\xb6\xb6~\xe3\u0253'U\xb7\u06ddH\xb5\xe9Dceu\r+\xabkx\xfc\xe9\xe7\xf1\xe7\x8d:\xf6\xeeZ\xc4\rG\x0e\xe1\xea+\xf6\xe3\xf0\x81=8\xb0g\t\xbb\x97\x160?;\x05%\x05\x86q\x828N\x9c\t\x12\x9c\u0740\vFH\xb5\xd1i\x13\r\xc5(\xb2t\x84\\x\xe8J\x9c\xfbqT\xc1:gU\x15gF]\xec*>\xe1l\v\x04\x11F\xe9vU\xb8x=7\x8aL\xd9v\xd5U\xf0\x8en \xaf;[H.\xe2\x94#-\xce\xcc[_\v\xf6>8\x9c\aA\xf86\xa7\xde8\xbdU.\xe4\\0{WS\xf8l2\x8fy\x8f\xb3\xdf\xe0\xf4\x99\xa8tj3\b\xb0\xb2\xbe\x89\a\x1e\xfd>\xbe\xfc\xad\x87p\xf4\xe5eG\x93i\b\xc1\xd0:A<\xb2\x15\xb0\xf1\x16\x84\x85\xd9)\\q\xcb[\xb1\xef\x8e\xf7\xe3\xc2\xc55<\xf1\xf9?E\xfb\x95\x97\x01\x00A\x10\x81\\t\x98]L\xc8k\xc62\xd8P\u039b39\x90\x19\xb7\x84\xe4Tj\xa8ch\x1d#\b\xeah\xcd\xed\xc1\xd4\xfc>\xcc\xee\xb9\x023{\xaeDki?jsK\x90K\x8b\xc0L\x13*j@H\xe5M(\xe7J\x8e\x04@;\x06\x86\x9c\xa0\x11)\xd4ju[\xc9;\x15\x0e\x91\xb0\xc0\n\x86\x12\xbe\xa3\xa2\x0fU\xa5\x05\x94\xfc\xc57\u007fOc\xc3\xe8'\x8c~l{\x1d\xbeY\x9d\x1a\x18\x84=\x03\xa1\x1d\x90\u01f6\xa9\x98V\xe8>\xedg_\"\xceR\x83X\xe44\"%\x8c\xa8\xab!\b\x186\xa4\x93\xa9\x02\xb1f\xf4\x13\r%\b\xa1\x00\x02%\xec\xff\x85\xf3\x96\xe1\\\x0f\xa6\x992/\x11.\x15\tCc\x1b\x9fd\x00\x15\xc36>\xc9\x1a\xf2\x11\x93]xD\xfax\xe1\x8a\x1d\xce\xc4\x05\xcc\x1a\xf5\xd6\x02\x9a3;\xb0~\xf1$dn@I\xa3\xd1\bI\x92\xecy\xf2\u0267\xae\x05\xf0\xcd3g\xce\xd07\xbe\xf15\xbc\xfb\xdd\xef\xb9\f\xe6?\xe8\xf1\x81\x0f\xdc\x03\x00\xfc\x89O\xfc\x1b|\xfc\xe3\xff}\xfa\xa1\xdbp\xa0\xfe\xe9O|\xe2\xdf\xfc\xbd\xaf|\xe5+w\\\xbcx\xf1M\xab\xabko\xbcp\xe1\x02\xf5{\xbd\x89M\xbb$I\xb0\xb1\xd9\xc6\xe6f\a\u03fft\x12\x81\x92\x98j5qx\xff.\\}\xc5~\xbc\xe1\xe0^\\}\xc5~\x1c\u063b\x03{v,`\xba\xd5p\xcd=\r\x8c\x12\b\x9d'\xe80\xa7\xe3\u031c\r\xb5\x14\xa8\x89\u031b\x9c\xf3\x06\x90\xc3\b]\xb007\xd9$\x93\xd7\x12t\x80n}2\xa4I}hr\xd0\xcf\x03+<\xbf\rg\xf3*D\x1aO\xe6\xd1\xf6\x94\x876hf\x1b\x19\xc6~\x02\x0fg\x95O1\u0601=f\xd8\a\xe2\xb1\xfek\xc1\x1f\xc5\xdf\u044cW\u0754\ro1\xb8\xe0\x86)\x85D-\f\xd1\x1f\x8d\xf0\xc0c\xcf\xe0\xab\xf7?\x82\x87\x9ex\x16\xdd^?\xb3\x9a5\xdaN[\xdaj<\xff\r;\x17fp\xed\xdb\u07cf\xddw\xfe4\xe6n\xbe\vb\xf7aLo\xf4\x10\x1c\xba\x01\u01fe\xfcg\xb8\xf8\xec#\xe8\x9e_\x06b\xbf\xa9M\xb9#\x1f\u0195*Y3\x8d\xf2\xf7\x8b@PA\x1d\xad\x99]\x98Z\u060b\x99\x85\x83\x98\xdbq\x18s;\xaf\xc0\xcc\xce+P\u07f9\a\\\x0fa\x8c\x86\x96\x8c$\x12\xd0!Y\xd9\x1e\xb4\xf5\xe0\xce$M\x02p`\xcdB`(m\xfa- Q\x93n'\xe6^#\xe9^?\u9a38K\x1a;0C\x1b{\xbf\xc1Q\x1a\xfd\x84\xd1\x1d\x19\x8c\xb4?=k\xd5Bj`,\x00k\v\x922f\x88\x84=\x0f\x1a\x14\xf5/\x8e\xff \xb2\x14\x87\xf6\xfb\x94\f\x88\x04\bz\x06F\x10t]\x14*\xad\xc40b\x03\x90\x03\xf6H\x12j\xca\xfe_\n\xbf7\xe1\x9e\xd7S\xe4\x8c\f\xa3\x97\u061d\x84t\u7656\xf0\x04\x80\xd2\x05\xb9\x9c\x15\xea\xed\xb6\xb5NPk\xcc`jv\x17\xd6\xce\x1fw\xbb\r\x06\x98\x89\x99\x93n\xb7\xa3\x9e|\xf2\xa9E\x00\xe8\xf7\a\xe2\xdd\xef~\x8f\xbe\\\x99\xff\b\x8e\x14\xc8\xff\xe0\x0f>\x81\u007f\xf1/>\x8e\xcf~\xf6^\x10\xd1Y\x00\u007f\x04\xe0\x8f\x1e}\xf4\xa1k\xfe\u077f\xfb\x93[VVV\xff\xeb\x97^|\xf1}'N\x9e@\xbb\xdd\xc1p8,|\xd8\xfdJ\xd8\x18\x838f\xacmlbmc\x13\x8f<\xf5<\xa20\u011e\x9d\v8\xb8g'\x0e\xee\u06c9\xc3\xfbw\xe3\r\a\xf7\xe2\xe0\xbe]X\x9c\x9bA-\x8a \x94\x1d\x821\u0330\x8e\xa6\u059c\xc9h.\x86\x9dL\u0a33\x01!\xbf\xd8c\xf6\x92\x93\xbc!$\xa4`\v\x8c\x91\xb6Y\x95h\xe0u\x87\\Ui\n\xbc\u007f\xce\xf2\xf8q_9H\x1b\xe61\xe0\xf2\u01f0\xfd\x19W\xeb#\x9d7\xa4*\x98\x94L\xf7c|\xf6\x99\xf2\x9d\a\xf9i\xf0d\xd5\rRZ\x99\xa11\x8cg_:\x81\xaf?\xf08\xee{\xf0\t\x9c\xbd\xb8\x9a-L\x16\xc4c\x8cFCho\xf6`\xef\ue778\xfe\xdd\x1f\xc0\x9e\xb7\xfc4\xa6\xae{\aF3{\xd0\x19&\xc0z\a2Px\xc3\xdd\x1f\xc1\xee7\xbd\x13\x1b'\x9e\xc3\xe6\xf21l\x9ez\x01\x1b'\x8f\xa2{~\x19\xfd\x95\xb3\x88\xbbm\xb0N\xf2IJ\x12\u05a4*\x8clcR\x86\b\x82\x1a\xa2\xda4\x9a\xd3;05\xbb\v3K\x870=\xbf\x0f\xcd\xd9\x1dh\xcd\xecFT\x9f\x86\xd61\xb4\xb1v\x13\tb\x98@\u0606]\fH&P `j\xcaV\xd9BZw*\x91Rz\xf9\xffcfl\x0e5FZ\xa0\x19\nD\n\x10\\}Gqe\x13\xdcI\a\r\xb2]d\xac\x81^l\u040dm\x8f\xa3\xb0^\t@\xc4\x06AGC\xc6v7*c\x03\x11{\xd5xy\xf5N\xdf>c\xf9n\x86\x05t\xf6\x82V\xc0\x96\x9e\x89\xba\x1a#\x02\u26a8\xf4\xeb\x1fiF\xac\x19\x83\x84\x10J\xa0\x11\b\u02f1\v.\xb65\x98\x900\xd0\x191\x86\xc6\u07a3rd\xad\x16\xb2\u05cfE6\x8b\x90G\x8cZ\xc2\\x\xeb\xb3\xd11\x1aS3\x98\x99\u07d5\xd1o\xe9g3PJ\x9cy\xe5,\xae\xbbnxC\xeao\xfe\xd0C\x0f\x8a\xdbn\xbb\xe3u\u03dbo\xcb6\xeeW\xbf\xfae\xfa\u02ff\xbcW|\xeaS\x9f\xd6y!\u008dO~\xf2\u007f\xffo\x9fy\xe6\xd9\xdf~\xf8\u11e7\x8e\x1d;\x86N\xbb\x8d$\xd1\xd6\xf3e\x82)\xbe\x10\x02J\xda-\xad1\x96Z\x10\x044\x1bu\xecX\x98\xc3\u03a59\xec\\\x9a\xc7\u03a5%,-\xcec\xc7\xc2<v\xeeX\xc4\xc2\xc2\x1cj\xb5\xc8\x03\x1b\x03\xad\x1dw\xab=\xadqa\x94\x99K\x1bl\xca8\xc8LaNEK]\xf2*g\xce'M\n\xc3(\xfeg\x88*\x02E)\x9b\xd2\xf0\x831\x8a\xfa\xef\x8a\x10\xf6\xac\xfa\xcf\x15{4a\x81*\xac\a\xde\xf3PQO\xef\xfdQ\b;\xec#\xa5\xc4(Np\xec\xe4\x19|\xeb\xc1'\xf0\xd0\x13\xcf\xe1\xc4\xe9s\x88\xe3\xc4z\xa8\x18\x8dD'HF#\x8cF\xc3\xec5\\X\x98\xc7\xcd\x1f\xfc\xfb8\xfc\x9e_D\xe3\x8a\x1b1\xaa/\xa07J\xc0\xa3A\xeec\xeev#2\xacAE\x11d\x00\x8c:C\xf4\xd7/b\xd4Y\u01e8\xbb\t\xdd\xefB\x0f\x870I\xec\x02#L>\x16o$\x1a}B\xa4\x15\x94\x88\x10\x85M\x84\xb5&T\xd8t\u05a91L2\xb2\x8dNA`!\x00%\xc1\x81\x80\x8e\x14L(\x01!,\xff+\x04\x92\x86B\u04b0N\x86\u9389\xb9\x1a\x96\x99m\xe3\xb0\x11\x10\xeaJ \x90\xf9\xa0\x94\xf4$\x82\xe5\x06\xa7\xa3\xa0\xed\xbd\xec\xaa\xf1\xde\xc8`\xa8y\xec\xfe`\x02\x84\x01\x82\xb6F\xd0\xd3\x10\x86!b\x86\x1cq\xd6\x14\xcf\\\x0f1i8\x8d`\x84\x15\f\xb1\xeb5\xd8F\xa5\x00\xa4\x95\x12&\x11a\u0612\xd05Q\x88\xa1\xe3\n\x95\xad\x10@\xa4\b\r%\x10\xcaT\x82lw\xbb\xbd\x98\xd1M,\u0146\xde\x00\xc1\xc5.\xe4\xc0\xa6\x96q\x92\x80\xfa\t\x10\x8f\xc0.\x93\x95\xbc`\x12\x99\ue01d\u02a81=\x83'\xbe\xf3\x17x\xe0\U000df10a\x1a A0\xf1\x10\xa3a\xd7H)\xc4\xdd\xef\u007f\uf4df\xfe\xf4\xa7>\xb2\xb4\xb4\xeb\xb9O\u007f\xfa\x93\xf2\xd7\u007f\xfd\x9f\xbc\xee\xabs\xb5\x1dO\xea\xbd\uff5b\x01\xe8\xe7\x9e\xfb>}\xf5\xab_\x13\xbf\xf1\x1b\xff\x94\x88\xa8\a\xe0\u007f9w\xee\x95{\xbf\xf0\x85/\xfe\xd2\x03\x0f<\xf8\x8bO>\xf1\xe4-O>\xf5\x14\rG\xa3L\x97^>\x8c1\x18\xb9h0!\x05\xa4\x14\x90B`0\x1c\xe1\xf8\xa93x\xe9\xe4i\b!Q\xaf7\xd0l6\xd0l4\xd0j6\xb1\xb40\x8bC\a\xf7\u16ab\x0e\xe3\x8a};13\u0570\xe3\xe4A\bU\x93\xaeq\xe3\x05,\xa4\xd5\n\x91\xa7\x84\xb0\x1a\xe71\xb5\x01\x8a\x86\xec\xecM\u0325\xdbg\xe3\xbaF\xb9\xe6\xdd\xe4A\xbf\xde'%\xeb\xe6\xbb'2\x15\nG\x1aSux\xe9=\xe4\x19&\xf9\xfc{\x19\xd0K\x95\xba`\x1fp\xf2\u6c12\x12\xb5(\x80\x10\x02\xbd\xfe\x00\u03fft\n_\u007f\xe01<\xfa\xf4Q,\x9f\xb9\x88\xd1h\x04\xa5\xec$G\x1c\xc7\xd0\xfbeH\x9c\x00\x00 \x00IDATI\x8c8\xb1\xd4\n\x00\x04a\x88\xeb\xde\xf7a\\\xff\v\xbf\x8e\xa9\xabo\x83\x0e[X\x1b\f\xc0\xed6\xa4\x80\xf3\xfe\xf6w\x04\xf6C:\x1c\xf6\xc1`\b\xa1Ps\xcdH!E\xee!oRE\x86\x81a\x031\x18!\u060c\x11n&\x90#\r\xc4\x1a&\x89\xc1&A2\xec95\x8etS\x98\n\x10\x04\x0e%X);\xd0\x04\x01M\x02Z\u0694\x1db@\x0e\b\xa4\x80\xa4\x96\x0e\xeeVF!g\u02a8\xd806\x87VO\xdd\f\x05j\n\x19\u0152\xca\xfa\xd2j7\x9f\x9a\xb5\x80\x15\x1bF7\xb6\xb4J\xb9\x1a\xf7\x95\xb3AW#\x18\u0608<J\x182\xe6<]\b[[Ld\x16\xb8\u0300\xd3\x0f0\xa5\xd7f@F\x80%C\xb9\na(\x00\x1d\x89\xb1J\u045fk\xd2\f\xf4b\xc60\u046eB\xa7l\xaax\xe4\x94UBH\x90\xb6\xde\xeb\xf6\x17\xb2\xdd%\xe4\x19w\u07adL\xdeN\xd1\xf5\xaf\x8c\xa52\xebS\v\b\xeb-h\x1dCP`\xfb\x14$\u0120\u07c71|\xe3\u007f\xfa\xf2\u007f:\x02\xe0\xb9N\xa7#3\xd1\xcee0\xff\xf1\x1c\xd7\\s\x1d\xa7/\xf2g>\xf3\xef\xe9\xa3\x1f\xfd\x15\u07b9s\xcf\t\x00\xbf\xc7\xcc\u007f\xfc\xb1\x8f}\xec\u007f{\xea\xe9\xa7\xff\xe1V\xcf!\x84\xa3\x05\xd8\xc0$\x1a\x80\xcen\x80T9\xc2\xcc\x18\f\x87\x18\x8eb\xac\xacm\x82aG\xc4\x1f|\xec\xfb\x98\x9aja~f\n{v-\xe2\xf0\xfe=8\xb0g\av,\xcea\xbaUG=\x8a2O\x10)\\\xb5m\xf2\x84y\xadmXp\xa2\r\xe28A\u007f\x14\xa3?J\x9c\x0e\xb7\xa8\xb2\b\x02\x850P\b\x02\x05\xa5\x94\x9dnt\x93\x8aRX\x90\xac\x87\x01\x820\xb0\xbf+\xf5zv\xc6V\x89\xfb\xbd\x99\tQ!\xb9)\x9b\xe2(*<\u01a2\xb9\xa8z\x14\xc97\xbeB.+3\xae\x19,%!P\n\x81R\x88\x93\x04\xe7V\xd6pr\xf9\x1c\xee\xfb\u0793\xf8\xde\x13\xcf\xe1\xec\x85\x15\x8c\\%N`\xf4\a#\x9b0o\\\u2f63Uv\\\xf3&\xdc\U000abfc5\x83\xef\xfcyP=D\xbf\xaf\xa1\xbb]\xab\xec\x91b|w\xe0\xb7\\\u04e1 \x00&\x19\xc1$\xa3\x12(\xe5CJrd\xa06c\xa8n\x02\x8c4Lb\xab<;\xb4\x13B\xaa\xa8\xc0\xa5\xdb\x12\x97\xc0\x81\xb0\xf4\x89\x93\xee\x89! \xb4q\x13\xac\x96X\x16\x9b\xdaR\v\rY\b#\x99$+e\xd8\x11\xfa\x91\u05b6b\r\xac\"D\x11\xf9Im\x19\u0367\r0L\x18\xdd\xd8*U\xc0\x13\xbcY\x18\b\xfa\x06AO\xdbsM\x81\xdcp\x85\xe72\x8f-\xe0e\xf5Sj~\x96%~ymK\xe9\xaaa\x100$\x82\ti\uceb3\"\xc0\xbb\x8e\xbe\x19\xb7#H\U000e90c4@i\xff!\xfd\xbd\xec\x87\xc5\x18\x17\xe8\x01/!>\xed\x93\x10t\x12\xa31\xb5\x84\xe6\xec\x0e\xac\x9d?\x01\xa9\xac\xc1\x99\r\xf3`>w\xfe<=\xf6\xf8\x13\xfb\x00\xe0\xaf\xff\xfao\xfe\u007f18\xa4^/'\xfa\u044f\xfe\n\xdf\u007f\xff}\xf8\x9b\xbf\xf9\x1b\xfa\xc4'\xfe\x80\x01t\x1ez\xe8{\xbb\xfa\xfd\xfe\u011f\x99\x9e\x9e\x02k\x8dv\xb7Wb&\xb8\xf0\u007f\xfbB\x10\xa4\x12\x90\xceG\x99@H\x92\x04\xab\xab\xebX]]\u01f1\x13\xa7\xf1\xe0\xa3\xdfG\x18(\xd4k\x11\xa6\x9a\r\xcc\u037405\xd5\xc4T\xa3\x8e(\n-\u007f9\x8am\xda{\xac\xd1\x1b\f\xb0\xd9\xeeb0\x8c\x11'1\x86\xa3\x04q\xa2\xb3\xada\xda\xe0\xb4\x15\xad\x82\n$jQ\x88F\xbd\x8eZ-\xb4\xff\xa6\x14j\xb5\x10\xcdz\rK\v\xb3X\x98\x9b\xc6T\xa3\x810\f\x10\x86\x01\xea\xb5\b\xf5Z\r\xf5z\x1dQ\xad\x06%\t\xacu\x9a\xae\xe2\x00\x1e\x05c\xbd\xdcn G|\xe6\x92\x05\t{4\x8e\x10P\xca\xeehD\xf6\xe5*\"\xad1\x18\x8epqm\x13\x17.\xae\xe1\xf9c\xa7\xf0\u0413\xcf\xe1\u0157\x97qqm#\xa3S\u0229\x8f\xb4\v\xf05i\x90/\x80\xe6\xd2^\\\xf5\xc1\x8f\xe2\xda\xff\xe2\x9f`\xe6\xe0!$CF\xbc\xd9\x05\x1c%\xe6\xef\x0e\xaajH\x1f\x93\xb8\u0510\xcd\xf6/\xcc\x0e\xc8\x19\xe1\xa6F\xd03\x10\x9a `\xabo\b?\xb0\xb0b$K\xd8\xecI\x03\xdb\x03a0(a(M0\x92a\xdc\x00\x81\x18\x01\xa1\xd1 \x06\xe2\xba\x04\xcb\xf1V\xc8\x18\xa0{\x15\xebH3\x1a\x01\xd0\f,\xf5B\xb9t\x05\xb1\x01:#\x83^l\x90TU\xe3\xfe\a{`\x10t\xb5\x95\x1d\xb2mv\nS\xe2\xcch\xf29\x15-\x12\xca\n\x1dW\x1c\v7|\xa5\x19$\x009d\x84\xd0\x18MI\xe8\x80&)XK\xef\u0378\x92V&\f\xa9\tl\\\xe4\x9csG\xf4\xcf'\x9d\xfcMUVy7\u01e9\xc6t\x82fs\x1e\x8d\xd6<V\xcf\x1esiM\x96^S2\xa0S'O\xa2\xd3\xe9\\\xcf\u0312\x88\xe2\xcb`\xfe\x13>\xdef\r\xe5\x19\x00~\xeb\xb7>\xfe\xae\v\x17.\xbcWO0\xea\xaa\xd5j\xf8\xd5_\xfdU\\\xf7\xc6k\xf1\xf0\xf7\x1e\xc0K\xcf?\x8bg\x9f?\x8as+\xeb0\\\xad\x86a\xa7~\x10d\a^\x04\x89L\x1eH\x00\xe28\xc6h4B\xa7\xdb\u00c5\x95\xb5,\xce\xcb\xdfG\x8e\xd1\x14\x9eJ\x80\xbd\u01a7?\xafY\u04a2\xb9\x9b]\xe4zv\xb2\u0a64\x95\xf4EQ\x88Z\x14\xa2^\xafaz\xaa\x85\xb9\xb9\x19,\xcc\xce`nv\x1a\v\xf3\xb3X\x98\x9d\xc6\xecT\x1d\xf5(D\xb3Qszn\x02\xb3\x86N,U\x834\xa6\x8cr\x85G\xea\x13nw\x03\x02\x81\xb2\xe9\xef\x83\xe1\b\xdd^\x1f\xbd\xfe\x00\xfdA\x8ca\x1c\xa3\xd3\xed\xa3\xd3\xeda\xb3\xd3\xc3\xca\xea\x06\x96\u03dc\xc7K'^\xc1\xb9\x8b\xab\x18\x8eF\x96\x0er\u05db\xc4\xec\x00<\xc9\x00\x1c\x00\xa6\x16\xf6\xe2\xd0-?\x85\xab>\xf8+X\xbc\xfd\x1d0\x01a\xd0\xeeY?\xefR)KeN7\x1d<)\xb9\x8b\xf9\xfd\x85\x02R\t\xeb1\x12\xb65T\xdf5\x005;\x90Hw&[E\x9c\xb8II7\x1e\u039e4U\xb8\x0e!K\xcb/\x8b\x84\x10\xb65(f\xc4M\x01\x1d\x8a\u0262\x94\x12\xb8i\x03l\x8e\fb\xc3h\x05\xc2\xd2\x10\xb0Z\xf1nl\xa7\"\xb94\xa4VFb94\b;\xda59\xd9\xc6\xe0\x19.\xc8E\xfd\xac\xd9\t\xb0\x8a\x02\xda\x13\x17\xe5K\x94N\x8d\xda\xc5M\x18\x00\x9a\xa1\x86\xaeBoI\x98\x90^\xd58N\xf6\x16\xa6\xebfl\x157\xec\xe5\xa3\xda\xe7\x19\xf7\xd5e\xce\xfbE\u067bG\u05b4\xab\u079cC\xb35o)!JS\x93\x14HJ\xb4\xdb\x1dlll\xdc\u011c\xec\x06\xb0|\x19\xcc\u007f\xc2\xc7\xfd\xf7\u07d7\x02:\x9e~\xfa\xfbw\x9e8qb\xac\xdaN\x8f\xa5\xa5E\xfc\xf2/\xff\x03\xdcq\xc7[\xf1K\xbf\xf4\x0f\xb0|\xfc(\x8e>\xf90\x8e>\xf74\x9e;z\x1cO=\u007f\f\xc7N\x9d\xc5z\xbb\x9bU\xcb:\u06da\v@*[\x89\xa6C<eg\xbe\xd4Od\xcc^\x94\u01abBx\n\x13_\x93]\xd0g\xa7#\xe6\xb6\xda0.F=\xdd\xd2j\r\xc4q\x82\xc1p\x04\xd1\xed\xe5\x8bM\x16~k\xa9\xa1f\xb3\x81\xb99\v\xe8\xadF\rs\xb3S\x98\x9b\x99\xc6T\xb3\x8e\x1d\x8b\xb3\u0635\xb4\x88\xa5\xf9\x19DQ\u0359`q\x81\xe3g6\xe8\xf5\a8\xb7\xbe\x86S\xa7\xcf\xe1\u0339\x15\xac\xaeobec\x13\xedv\x0f\xdd\xfe\x00\xfd\xc1\b\xedn\x0f\xbd~\x1f\u00e1\u0749h\xadsN\u05f8?\x1bc\x17\x10\xb7KH\x8f\xf9=Gp\xe4\xb6\x0f\xe1\xf0\x9b>\x80]\xd7\xdc\x01j\xd4\u047b\xd8A\xd2\x008\x14\x95\x01\xa8\\\n\xf7\xf0m\x1a\x88\xb6n\xef3Y\x05G\xd8\xd6\b\x06\xc6R\r&\xa5\x1c\xb82\a\xb6 F\xa5\xbc\xca$O\xd2d\x03\x14\\\xc3\u05cd-\xb0\x15]\x804\xdb\xe1\x1c\u0348\x9b@R\x13\u0145g\x12\xa8\xb9\xe7\x1b$\x96:S\"w\xaa\xd4\x06y\x90\xf5\x84'\x101#\xec\x1a\x88\xc4%\x1d9\x90%FiBw\xb2\x1d\x1d\x97UR\xec\xe3\xa8'ka\xef\xe5q[;b\v\xe8L\xc0\x88$\x8c\x12\x93\xb8\xb1jZ\xd4 W\u06a4U\xb8\xc9\x04]\xee\xf3\xe6\xfc[L\xeeL\x99\xf6\x15\x84\xa3MM\xa2Qk\xb4\xd0h\u0343\x84t\x16\xb8\x80\x94\nB(\x8cF#\xc4q|\xeb\xbd\xf7\xfe\xd5.\x00\xcb\x0f<p?\xee\xbc\xf3m\x97\xc1\xfc'u\xfc\xee\xef\xfe^\n\xdc\xc1;\xdf\xf9\x8e_6f\xf2M\xb2w\xef>\xec\u0673\a\x80A\xbd\xd9\u01357\u074ekoz\x13\xd0>\x83\xd5S\xc7\xf0\u02a9\x97qj\xf9\fN\x9e>\x8b\x93\xa7\xcf\xe2\x85\x13\xa7q|\xf9\x1cN\x9f\xbb\x88\xf5v\x0f\x83\xe1\bZ\x97\xbb\"\"K\x11\u03ebv\u1188\x8a9\x9fy%>>*N\xbe\u0169\xd7\xc8\u02dd\xf8\xbcf.\xe5\xfc6g\x9ab\x064\xd94\x18!A\x8c\x8c\xf6\xb0\x15\xc7&\x8eq>d\xa4\x94\xa5\x86\xe6fZX\x9a\x9f\xc3\xd2\xc2\x1c\xe6f\xa71;\xddD\xa3Q\x87\x92\x12Zklv:XYY\xc7\xea\xfa\x06V\xd77q\xe6\xfc\n\xd66\xdav\x98G\x97\x06\x83\x1c\xf0\xa7\x15x^\x89\xbb\u01b0\xb6C7\xa9|4\xa8\xb5\xb0c\xffux\xc3-\x1f\u0121\x1b\u07c3\x9d\x87o\x86\x10\x11F\xbdM\xe8n\x0fA @#\x81\xe1L\x80$\x12\x05Kc\xc03(D\x01G&\x03c>(\t\x91\xc0\x02y\xca\x1fk\x86(\f\u028c\xc1wE\xa9_\xfd;\n\x14\x90\xb6*r\x1d\xe4\u0563\x1c\x1a\xfb\xbb\x12\x89\xa4!ad\x81\xa6\x1e\xe3\xd3\xfd_\x17;\xbdv\n\x98[Y\x880Yz\"\xecj\u0221\x9b\x99H\xc3\x1bR\u0547\xd74\xa7\n\u0687\x8a\xec\x9a#\xaa\U000f26483O\x98\xbc\x9bO)]\x9e\xff\xb0a\xa8\x81\xfd\xe4\x8cZ\x04\xad&\xa6\u064d\xbd\xb4\"a+I4\xb0\rh$\xb6\xd9\xee/=\\\xbc\x0fQ\xb8'\xf2\xc9U\"Bsf\x11a\u0530>\xf1R\x82\xa4\x82T\nF\x8fp\xfe\xfc\xf9\xf0\xa1\x87\x1e\x9e\x02\x80\u007f\xfd\xaf\xff\xe7\u02d5\xf9O\xf2\xf8\xdc\xe7>\x0f\x00\xf8\xecg\xef\xbd\xee\u0739s\xd7UY\xe7\xa6\u01d5W^\x81\xfd\xfb\x0f!I\x86H\x92\x04\xf1h\x04&\x01\x11.b\xf6\xcaY\xcc\x1f\xbe\x16\xd7\x0f7\x80\xf6\x1az\xed\rln\xaec\xb3\xdd\u0145\x8b\xabx\xf1\xc4+x\xe2\xf9\xe3x\xf4\xfbGq\xf4\xc4+X\xddhc0\x8cm\xcf\xdc\x15\x98\xaf\xad\xf5M\x90*\xb4\xcd\u0634\xbc\xa4t@Hd\xa1\x05 \u06f0e\xa7\x90I?\x18\xbe\xb9\xb6o}k\xaba\x032n\x98HHk\n\xa6\x14\x94G/23z\xfd>:\x9d.N\x9c:\x03\x80\x10\x84\n\xb50D\x10(\xeb:\xa8\r\x06\xc3!z\xfd\x01\x92D[e\x88\xa3w|\x90N\x17\x14\xceF\xfeM\xd1C$=m\x93@\x06\x11\xa6\xa6\x96\xb0t\xf0z\\u\xf3O\xe3\xc0\xd5o\xc3\xf4\x8eC\x10*D\xdc\xeb\xc1\xe8v\xb6\x18R\xcc\x10Z\x83\fa0\xa7,\xa0W,\x86\x85`\x03.J\xec\xc7\x12\x9a\x04\x814#\xea$\x19\x90\x93\v\x00\xceS\x16\x8a\xae\u06d5\u068e2\x8ap\x1emG\xc6n\xe4\xf2\tIKk\x18IY$'%@\xd8\xd5\x10\t#n:\xfa\xe1U\x98\xc9\xd3%\xf8e\x1f\u0205\x01\x82\x9e\x86r;\x8fl\xf4^\x97\x16\xad\x94&\xc2d'\xd1\xfc5\xa6bk\xb420%\ud193\xa5FR`w\xe7C\f\f\x1d\x87Nf\xeb\x8b%\xb6\xcdi\xa1\xd9J \xd3\xe9X/\xac\x84\xbd\xc72\x8d\xf9D{V\x04V\xc9Vo. \xacO\xa1\xdfY\xb3\x86f$ U\x88x4\xc0\u0253\xa7\xf0\xfe\xf7\xbf\xef=\x00\xbe\xfew\u007f\xf7\xf9\xcb`\xfe\x93:\u039cY\xc6\xee\xdd\xfb\x00\x00_\xf8\xc2\x17\u007f\xf6\u0739s\x98\xa4-\x97R\xe2\x96[nq\xa0b2\u054a\xbd#\x04b\xaa\x01\x14\x81\xeaS\x90\xd1\x0e4\xe6zh$\x1d\xec2#\x1cIb\xbcm\xd4G\xd2\xedb\xb3\xd3\xc1\xcb\xcbg\xf1\xcc\u0457\xf1\xe4s\xc7\xf0\xfc\xf1e\x9c\xbd\xb0\x86\x8dN\x17\x1d\xc7!\x0f\xe3\x04\xc3QR\x18t\x91B@)\t%\x05jQ\x00\x90@\xb77\xc4(\x1e\u066a^\b\b!a`\xacQ\x94Kf1\u01a0\x16E\u0635k\x17\xc2(\x82q\x89\xf2\xc3\xd1\x00\xf1(vZ\xec\x11\xe2$\xc1h\x94 \xd6\x1aqb\x15%a\x10@\x90\x80\xd6\x15\xed&_\u0152\u068b\x0e\x86\xe8\xf5\xfa.\x1d\a\x19\r\xe2\xeb\x9b\r\x19\x90.\u058by\xb2\x0eg\xbax\u03a6f5\x8c1\x88\xea\xd3\xd8s\xe8&\xec\xbd\xeav\x1c\xba\xe1=\xd8u\xc5\u03687\x16@\x10\x88G\x03\xc4\xfd\xae\xe5\x9aS\xe3a7\x80\x02M\xa0\x8e\xd5\x15\xf7\x17\x03\vz\xba\xbaI\xe61\x1f\x05\xed6\xf2\u034d\x05\xf2v\x82\xc0M>\xc2\x01\x1c\x95\x00\x8e\xb6\xe4h\xb6\xc0\x1f\x93\x03\vL>\x1cF\xda-\xb9\xa9d\xc9X^=\xe8k\u02041jJK\xbb\x88\x92\xf1&.\xf9+'\x82`\xd03P}\xe3\x1a\x9e\x94/Zi\xdf\u0420\xb0k\x9c4\x92\xe4\xb3+9\xb7QvQ+\xe6\u0125\xf4\x910\x06\xd0\xc5\xd7M&\tH3\x06\xb3\nZ\u0456\x15\xbaH\xac\xdd.\x19\x17\x00\x92\x1a\xaa\x15\xe6\xde\xdct\x03\t\x10\xe5\xc5U\xc1\x84\u051d\x93I\x124\xa6\x16\x10\u0567\xd0k\xafdW*\xa4\x02\t\x85\xcd\xcdM\x1c?\xfe\xf2\xad\xcc<OD\xab\x97\xc1\xfc't\xa4@\xce\u0335\x9f\xfb\xb9\x0f}tk\x8ae/n\xbb\xed\xcd\x13\xca\x18\x93\x99T\xb3\x90HD\x1d\x89\xac\x03\xc1\f\x90\x8c@j\x04\x19\x0e\xa1Z\t\xe6wj\xcc\x1f:\x82[n\xbb\x15<\xe8\xa1\xdf\xed\xe2\xfc\xca\x1a\xce^\\\xc1\x85\xd5\r\\\\\xdb\xc0F\xbb\x8b\xcdN\x0f\x83\xc1\b\x86\x19JI\xb4\x9aM\xabYo6\xb1w\xef^|\xf7\xb1g\xf0\xc7\u007f\xf2\xe78\u007fq\xc5\x15\x8c\xd2\xd9u\xdaiN!\xed\xec\xf4p0\xc0\xee\u077b\xf0\x8f\xfe\u046f\xe1\x9ak\xae\xc9\u0498666\xd0i\xb7\xd1\xdeX\xc7\xfa\xfa\n\xba\x1b\x1b\xe8t\xdahon\xe0\xc2\xca*\xce^X\xc5\u064bk\xd0\xed6(\b\x10\x86\xa1m^f\x82\x04\xbf\xb2v\x9bQ\xe3{p\x97'\xdc\xf3*\u06ceG\x9b\x8c\xf7\x96R@\xa9\xc0N\xda:E\n{\xde\xdd\xe9\x13\xde\xf13\xff\f\xd7\xde\xf9\v0\x89\x8dS\xd3#;\xb8\u00de%\xad\x1f\x94\x93\xc9#\x13 \xechp@\xe8-\x04[\xf2\xccU \xe4\xf3\xe9AO[EG*\xe1c[\xc1\x96\xcb\xee2\xddP\xf0\xa2'\x94\x02 \xbc\xfeFy\xcd\xcc\xc7\x12!\x98a `T\x9e\xbeC\x06 c\x10i\x86H$\u2980\x91Te\xbb\xf2\x9a\x00]\r\fT\xcf5<\xb3\x99z\xce\x16\x9b\xaa\xe9\x9d*\x9a\xc5_,\x8btU\xeeMO\xbe\xe9Z\xbai\x14\xf9\xe2&\xd2\xd73}\x0e\rD\x1d\r\x10a0+\v\xd7[\xb8V\xaf*O\xe9\x1b\"\x012\xf9 \x1c\x15,,+\x9a\xa1\xeewKw\x83i6\b\x1bM\x84a\u0765G\x190\v\x80\xac\xaa%N\x12\xb4\xdb\xed\xbb?\xfd\xa9?n\x02X\xfd\xf6\xb7\xbfEo\u007f\xfb;\xf92\x98\xff\x18\x8f\xbf\xfd\xdb\xff\x17?\xfb\xb3?\x0f\x00\xf8\u02ff\xfc\xf3_|\xf1\u0157\x0e\xb9@\x8b\xca\xe3\x8a+\x0e\u399bn\xb6\xb4\u0224\xe6\v3\xc0:\xbfcI\x02A\x03\x1c6\x900\x90\xc0~_\x98\x04\xb2\x16CL\xc7h\x98\x11\x0e\x1dHpH'\xf9\xa8}V*\n\xe7\xc5!\x01\x15\x022\x00d\b\x84\xf3\xe8\x87\u007f\x01\xf3'\u007f\x91=\xdc\xd2\x13:\xa3I\xa4R\x102\x00L\x02%%\xae\xbd\xf6j\xbc\xfd\xedo\xc3\xe6\xe6f\xe6p\u021cs\n\xc9p\x80Q\xaf\x8d^{\x1d\xab\x17\xcf\xe3\xe4\xa9e<\xf5\xcc\xf3x\xf6\x85\x17q\xf2\xf4\x19\x9c=w\x01k\xab\xeb%2\u0213tIi\x1b\xba\xe4\xbb4\xe6\x8dd6V}B\x04\u0522\b;w,b\u03de=\u0635s'@\xc0\x13\x8f?\x89\u04e7\x97\x01\u0599w\xb7\xcfs\f\xba\x1bX?\u007f\u00a93b$\x83\xbeK|O\xb7\xda\xc5Qr.\xd9\x13\x90f\x84\xed\x04IH\x18N\xa9\xca\u0127*\xd4\xf3\x15\x11\xaao2j#\xf3Ow\xf6\xa9\xe4\x05*\\r\x04\x9a_\xdd\xf7}\x95H\x8aod\f\x88\x05t\xe0Q.\xec,];\x8eviI\xe8\x90\n\xd2\xcbW\v\xe8L\x80\x1a\xda\ub509\x17rlR*\xc9\x1ad\tT\x84A\x8ceUy\xe2C\xf2c\xde\xc6\xe5/\x05j+\xe36\xec\xa0Nz\r\xe9N\x80\u025eO\xd0\xd30\x02\x18MI\xbb\xc0q\xf1=\x13\xb1\xab\xcau\xdep\x15d+\xf0\x14\xc8\xd9\u07ce\x19\xd7\xc4u;\u0374\x1a'o'J`\xa8Z\x13A\xbd\x99\xdd\u06e9m\x85\x94\x01t\x1c\xe3\xc5\x17_\x14w\xdcq\xfb\x8d\x00N\xbd\x9e\x81\xfcu\x03\xe6)\x90\x03\xc0\x97\xbe\xf4\x1f\xefn\xb7;A\u0784\x1b\u007f\xfdo\xbf\xfdvLO\xcf\u0098W)\x1fe.\xf2\xa1\x19\xaaH\x18)aT-\xff\x00\xb0\xb1_\xde\u0523eo\xdcG\x86\x84\xdbn\xdbG(\x002\xaaCJY\xa9\xbca\xb6\u0737\xfd\xbd\xc6\xf9\x93\x8c\x9c/L\xe2Gdf\x8a\x93\xb0\xdeD\xd4hazi7\xf6\\q\rn\xba\xdd\xe0\u00ff\xa8\xb1\xb2r\x11/\xbet\x1c'N\x9c\u00a9\xe5\u04f8\xb8\xb2\x82N\xa7k\a\xa2\x9c\xbcpc\xb3\x9d\xfd{\xa2mU\x9dv\xf9\xc30@\xa3\xd9\xc0\xd2\xe2\"\x0e\x1d<\x88\xab\xaf\xbe\n{\xf7\xee\u016e]\xbb\xb0o\xdf^\x1c<x\x00'N\x9c\xc4?\xff\xe7\xff\x1d\x8e\x1f\u007f\t\u02bb\xa6\xf2q\xfc\u026f\xe1\u01bb\xfe+\x84\xf5i\x18A\x99\xde:\xdb\xees\xa9\xa2\x857\xdc\x04\xfb\xe1\x8e\xda\x1aF\n$u1\x06\xe8\xe5\x14x\xdf\xdeD\x0es \xf7\x91\xd1R\x10\xe3,Ju\x95:)\xabjkb\x86\r\xe7\xd6\b\xce\a\x05,`\x82\u0519\xd2\x01\x9ea\x04}K\xff\x8cZrL\xed\xf2j\x80\\$\x8c\xa0ke\x96\x80\x1b\xb5w\x1c=\x00\x98t\x98\xa9\x1c\u00baU*I%A\xef5\x1f\xfd\u0184\x93\xefd\x03E\"\xc3\xf5\xfc'\xdd\xf7D\xc2\bz\x06L\x84x\xca\u06d1P^\x95\u02c4\u11feP\xea\xf4\u51842\xd5JV\xa9\vgfWm_\xcf`\x04Q\x1dA\xd8\xf0z,\xf63\xa4\x82\x10I\xdc\u01c5\v\x17p\xee\u0739w\x00x\u0753\xe6\xaf\v0\xbf\xef\xbeo\xe2\x1d\xefx\x17\x98\xf9\xc0\xddw\xbf\xff\xb6\xf5\xf5uL\xda%\xce\xcf\xcf\xe1\xfd\xef\u007f\x9f\xad\n\xf5\x0f2\xa1;f\x13X\xaa\xfel\xb50\xf6\xab\xb3F\xa5.\xfc\xac\x8a\xecdd\x10\x04c\x1d\xf8L\xf2\xe84\xe0\x96\x05J\x10H\x81(\n\x11\x85\xa1\u05ec\xca\x03t\v\x897\xee\x1f\r\t,\xee\x9f\xc1\x9e\xc3G\x10*i\xf5\xe1\x83>\xfa\xfd\x81\xd5\xc7\xc71\xfa\xfd!\xda\xdd\x0e\xd6V\xd7\xd0\xed\xf6p\xe1\xe2\nVVWQ\x8bB4\x9b-LOOaff\x06\v\xf3\xf3\u063d{\x17v\xef\xd9\r%\xc3\x02\xa0\x19\xc3h6\x1b\u0540\xe3}\xa2.,?\x83ao\x13\xb5\xd6|\xb6) S\xad\xe4\xe0B\x022y\x1fpv[tk\xe4\xe4\x03\x05U\x045A\xb8E\xa0\xe3\x14\x1d&'L\x84\x01D\x89\x9a+\xe7\xbf\xfaCU<\x11\xecP$\xec\xbd+\xc8\xdf%\x9fr\"\xc8\u0100\x98\xa0\xc3<S-\xa5\"\xe4\xc8 j;\u06a5\xe1@\xae\xd4s\xac\x02rb \xec\x19(w\x9d\x9c. Nv\xc9\xce\r\x91\fg=\x99\xccLml;\xe3]\u05eb$xrz$\x9f\x91H\xa7`\xcb\v,9u\x960l\xad\x05\b\x18\xb5\xf2k\x15\x86\xa1F%Z({\xaf\xc9\u04d0{\u0397\xbe\xcf(;\xa5\r\n\xf9\xe0v\xd7+\x03\x84\xb5&\x84\x90\xd9k\x04\x10\x84P \x92\x88\xe3\x04\xedv\xfb\xed\xccLD\xc4\x0f>\xf8]\xdcq\xc7[.\x83\xf9\x8f\xeb\xd8\xd8\xd8 \x00\xfc\x99\xcf\xfc\xfb\x0ft\xbb\xddk:\x9d\x0e\xa4\x94\x94s\xc0\xf9q\xfd\xf5\xd7\xe3\xb6\xdbn\u035a\x9f?\xfa\x83\xc7\xcc\xf1/u\xa4\xe1\n\x85f\x8f\x1b\xc9O\xc1<=W\x02\xa3^\x8fP\v#\x98V=W\xba\x94T\r\x13\xf3\x9f\x19\x18%\x89\x05\x91\xb0\x8e\xe9Z3\xf7A\x17\x02\xb9\x8b4\xd0\xedn\xa2\xd3\xe9bn~\x0ea\x10\x00\xf0+m\x8d8\x1ea\x10ws\xafv\"\xec\u0631\v\xd7\\s\r\xbe\xf0\x85\xad\xf3p\x87\xbd\r\xac_8\x8e\xe9\x1d\a\x1c\xf7\xa9\x8bzB\xaeh\xf9yZ?\"\xbb.\u02a1A\xe8\xc0/\xad\xd0=?\xb2\xfc\xda\xc9V\xdeaGC\r\xd8\xf1\xd3\xde\xebm\xbc\x99\xf8J\x19\xe28\xe5P\xdd\x13%\x94'\x97\xa8\x9c\xef\xea+/8\xe7\x93e\f\x18i\x95\x1a~\xcf@\x8c\x80P'\x19\xa0'\x91\x18\xabp\u02e7\x915<\xd3\x06d\xdah-{\x94\xbb\xc1\x1e\x9atY\u07a9\x17\xbc\x1a\xc6\x1a\xa5\xbe\xd8\xdc\xf9\xa5\xf89\xa0n\x95)\x18\xba\xa5\xef\x95qrA\xe7Q\x1e\fL\x01\xd0\xd5\xc8\xca\x11\v\xc6\x12\xee\x9c\x04\xa8@\x162\xb9\xb6W\x99=,uA\xd2A?A\x12Q\xad\t\xa9\x82\xcc?)-\x88H\x06l\x18\xb4\xbe\xbe~\x13\x80\x10\xc0\xf0\xde{?{\xb92\xffq\x1d.\xa3\x8f\x01\xe0\xfe\xfb\xbfs\xd3\xc6\xc6\x06\x88(!\"U\x06r\xa5\x14~\xfe\xe7\u007f\x0e33\xf3`N\xb6\xcd5\xc4\u039f;\x05D\xf2\x06\x91R\xfa%\xbd\x96\u010d\u29c0\x9fz\u02cc\x93\u00d3K&\x9f\re\x9d\x14\xa3\xbf8\xe7\x0eG\xa3\xa1\xb5!0\x06\x83\xc1\x00\xfe\xe2\xe8\x9fgff\xe4\x16\x9c;\xef\xbc\x13\xd3\xd3\u04d6\u04dft\xcd\xc3\x1e\xd6\xce\x1e\u00fek\xdf\x01\x01\x01\u05ba$s+\x0e\xe2p\xa9$\xe6\xd4\xc3\xddyZ\xa7M\xb4\xa4N\xb9%k\xa9\xb8\fz\x1aA?\x1fa\xf7\xb7\xfad&\x039\x95\xaa\xf3j\u04ba\xb8\xf8PE\r\xcf\xf0\ar\xc6w-V\xa5\xe1\x93\xea\xf9\x0f\x92\xb6<?%\fj\xb2\xb5\x02(\a\u007f\xbb\x1fUC\xcbA\v\x9b\u049c\rh\x8a\xc4y\x94\x9bTzY:\xb1TN\x99\xb1\x89\x159nTR\xafT*\xe0K\x04\xff%z\r)0\xa7\xfe*\xd0\f\xd5\xd7`\x01\xc4u\xe1t\xe5\x9cW\xe2\xc8\u04cf\u021f\xae\xf6\xa98\x93\xae_\x9c\x85W\xa7V\xb8%\xa7^\x04Q\vB\x05H\x86\x03/$\x05PA@\xc3Q\x1f/\xbcp\x94\xef\xbb\xef\x1b\xd7\x02x\xfc\xf7\u007f\xff\xf7_\xb7`.\xb6\xf3\xc9}\xef{\x0f\xe0\x8b_\xfc\xbc\xcd\x14a}\xd3\xfa\xfa\xfa\xddg\u039cA\x10X\x02\xb2\f\xe67\xdcp=\xee\xb9\xe7\x83\x0e@\xb7\x8f\u0742U\x83\x98\x02\xb5\xa2\x94D\x18\x86\b\xc3\x00\xd25$\x01`0\x18\xa0\xeb\xbcd\xb2\xc6\x0e\xe7\tA\xec\x15\x98\x93\xbe\x8a\xe0\ue0f2\xc8\x16\x10!\xadSd\xbd^/\x9c\x97\x942;\x9fr\xd0-\xb9O\xc1-\xb7\u070c\xab\xaf>\xb2\xf5\"l4:kg\xdd(5\xd2`\u0209\xe7X\xacx\x91i\rS)\xa1\x1c\x1aD\xed\x04j`\x8a\u041a6<\x9d\x17\x89\xf0\u0324\xc8G\xe8\t\x9e\xe1\xe3\r\xc7\xd2$\u0418/@\x99\xf2'g\x04@\x05&\xa6\xfc\v\u04a6+\x19[9\xdbp\b\u05f0t\ra\xe1\xc6\xe1\xa3M\x8d\xa8\xe3\u4525\u0758\x8c\x19a'\xb1\xa1\r\x8cl\xb1SCc\xadm\xc7v <F\xd10\x95\xae\u008bB,<\xd0\xe7z\xa8\xf4vy\xefW\xf1\xdby\fK\xc1r\xc1_\xb8\u063e\x06AW#\xda\xd4\x10\xc3\xdc:\x80\xb4\xb7\xbb\u0206\x95\xbc\xe9k\xce\xdf\x13.u\xbe\ty\xfaRZ\xb4\xc00\xa2\xfa4TP+\xd99\x1b\b!\x91$\x8cv\xbb=\xf5\xf8c\x8f\xbf\ue8c6\xb65\x98\xdf~\xfb\x9dx\xf9\xe5\x97\x05\x00|\xe63\xff\xe1\xc6\xf5\xf5\xf57t:]H)E\xf9\u62e2\x10\xf7\xdc\xf3A\x1c:t\xa8\xc4Y\xfe\xe7?\x92\xa4\xa8COu\xe6\xf5z\r\xf5z\x03J\xc9\f8\xbb\xdd.666~\"\xe7\xf5ZS\xc9S\x8f\xef\u00c7\x0f\xe3\xc0\x81\x03\x97||o\xf3|\xb1\xea+\x15v\xc5\x05\xa8\b<i\xb5I\x0e\x00\x89\xedV<h\xeb\xcc\xff#\x05\t\xa1\xed\xe4\xa3\x1a\x8d\x0f\x90\xa4\xcd\xc6q\xcb\xd7-\xb8\xf1W\xc5\x18\xa3\xb0\xa3\xca\u0399*\x00-\xc5L\x93'\xcb\v\xcd\x10\xb1\xc9\x12\u007f2k\x01\xb6\xbc\u007f\xd0\u0548\xda\xda\xd2\x0f\xa9E\x80\xb1\x96\xb6i?\xc0.\n\x06\"6N~\xc9\x05\xb9g\xa1\x97\xc1\xbc\u014e\x8e=\x15\x8b_N\x97\xab\xec\n\x13\x9c\t\xfd&\xae\xd0\x1f\xfa\n\xa6t'a\xdf7\xe3\xed\xa0r\xeb\x01\u02e1\xa7\xc1\x14\xfe\b\xac\xb7+\xa2\x1c\u0229\xd4\xc2b\xe3f7\xea3\b\x82\x1a\x8a\x96aY\xae\x17\x12mp\xf2\xd4\u0277\xa6?\xfb\xc2\v\xcf]\xa6Y~\xd4\xc7p\xd8C\x145\f\x00<\xf5\xd4\xf7oXYY\x013'\u032c\xca|\xf9\x9e={p\xf7\xdd\xefG\x18\xd6\x11\u01c3mu\x1d\xfd~?\xdb)\xf8\xd4E\xb3\xd9B\x18\x86\xd0:\xc1h4\xca\x1e\xeb\x1a\xbc\xdb\xf20\xc6 \bj8p`\xff%\x1f;\xe8m\xc0h\x03\x91\"o%\xe7_\b\xb5\xcc=V\xbd\xe6Y\x96\xbda,\xc5@\xd0\x18\xc2\xfa\x9d\x10[zB\x0e\xbd\x91\xf5\u0313e\\\xcd\xc1\xd5L\xc0\x04\x9c\xa3\xf1^g\x15\x1d\xe3\x11[\xfe\xa3*\x1d\xc2M\xf1y\u0205J\xb0\xb4\xd2M\xe3\xe8\x17\vt\xc6\r\x19\x01I$\x10\xf45\xd4\xc0NU\x92N\x17\x81*\x8e\xc8\xef\xed\xa0\xd2{f\xfcJ&\u0343\xfa=\x8e\\\xc0\x88R\x03\xba@:\xf9\x95\xbf\xe3\xd1\xd9\u04e2s*\xfc2p\n\x16O?\xc0\x1e%\x95Z\u0792\xa8x\xf2rR\x91\xd7\x1b@\xaef\x01\x03a\xad\x05\xa9B\xef\xc7)\xdd\x12A*\u0143\xc1\x90\x8e\x1f\u007f\xf9:f\x8e\x88hx\xe4\xc85\x97+\xf3\x1f\xf5q\uff5f\x15\x96\x16\u3e53'O\xbe\xf1\xf4\xe9W\x10\x04J\xa4^ ~\xa5\xfb\xaew\xbd\v\xd7]w\x1d\x00\xfdcj|\xfe\xe0G\xbb\xdd\xc6`0(\x809\xc0\x98r\x8e\x87R*\xe4\xd1b\xc0p8\xda\xd6=\f\x00\u063f\xff\x00\xa2(\u06aahE<\xe8\x01\xc6\xe4\f2\x97\x84\u0528\xb2\xf0\xab\xa2[\xf2\xc7\b\u00d0C\xa7X\x19\xd9\u051c\xa0\xaf]e[\x1a\vu\xe0^V\xd0T\xa7)Up\u0095\xc9;\xe3\xe1\xd5)\x9c\xa7\xbe\x8b\\\x1e\x83\xaf\u068a\x94+\xf6t2\u0564\x8b\x92\xfd\xb3\xa5\x974\xea\xeb\x89uB4(6w+\x1a\x80\xd9\x16!\xf5\xab\xa7\xf2\xe3h\x02\x17\xce\xc5\xef\x8f\xfdL\xfe\xbdI\xe2L*?\xbf\xef\xf2\xe0\x14/Y\xe5\xads\xc33\x91\x18\bc\xb2\xc57\u02f8\xf5\xa7\xb7Q\xf9\x16\xa3\xb8\x01b\x18\xef^e6\x88\xeaS\x90A-[\xf5\x84H-4\bA\x10Q\xb77\xc0\xa9S\xcb\xf3\xdf\xfd\xce}w\\\xa6Y~L\xc7\xc6\u01ba\x02\x80\xcf}\xeeoo%\xa2{677@D\xc27u\xb2U\xf9n|\xf8\xc3?\x8f\xf9\xf9%l\xe5o\xfe\x93>\u049b\xf0\xe2\u014b\x8ef\xf1\xd2\xe2\x19XXX\xc0\xe2\xe2\x12\xa4\x14\x85\x9b6\xbd\x86\xd7J\x83\xfc$\x8f\x03\a\xf6cvvv\xe2N\x1b\x00\xe2a\x17Z\xc7VZF^\xfd\xc6\xe3UVUm\x9c\x8e\xc9Sb\xb2*\x94\x9c\xc4P\x8e\x18\xd1f\x82h3\xc9\xd2s\xc8\x14<(\xf3m\xbb).\x1a%bd\x8c\"\x98h\x81[\xe2\xceil)\xe0\U00045a7a\x9e/^s\x89\x82\xb1\xf6\xafy*\x85\xdf/ ]\xc4V\xf6\xa5\x92\\\u0352\xe4\x97E\xd5\x15\xfaV\x8c\xe4\x18\xc5R\xf4\n*\xfe..\x18;\xfbt\fy\x14P\xba\xab\xf0\xcd\xce\xc8 7\xec\xf2{&\x94\xe7\v Wv\xe6\v\xa6\x97\x9a\x95\xf7\x96\u0606\u0478>p\u0618B\x10\u05b2\xe9\xe7l\x91\x80}nm\x80~\u007f\xb0\xe3[\u07fa\xef\xb6\xcb`\xfec:\xbe\xf2\x95\xaf\x02\x00\x9e|\xf2\xc9\xfd) \xa6\x1e&>\x98\xdfu\xd7]\xb8\xf3\xce;\xb0\x9d\x92\x9f\x98\x19RJ\f\x06=\x9c;w\xce\xed (\vv`6\xbc\xb8\xb8\x88\xc5\xc5\xc5\xec\u3402w\xa7\xd3\xde\xf6`\xbeg\xcfn\xcc\xce\u030c\u05fa^E8\x1at0\u8b3b\xc1\x92\xbc:\x1foA\x96A&\xafb\xd3\x11|\x91\x18\x88\xc4d`G\xda\x0e\xa1\x84\xa9\xef\x8aO\xf1\xfaO\xa7\x91+;x\x12R\x95\xbb{\x15 _Q\x02\xf3%8\xf7\x94N\xf0\xab[\xda\nI=u#a\xbcj\x17\x89\xe5\xd9\xc9\u016be\x8a\x98\x8ak\xcb\xcd\xcf\xf2\x05\x94+\x17\x13.]3UL\xc7\xfa\x12L\xaf\xa3K\x97\xc0\u007f\xef/i\x837\xdd}X\x130d\x8d\xf1|\xb9\xa3\xc2\xe2d?\x18\"\u05da\xfb\x97\xe4}>\xb2\u0701\xb1\xb4sF\x10\u05ad\xd6\xdc\x19\xd51\xe7=\x9c\xd4\xcb?I4\x8e\x1d?\xfe\xe6\xcb`\xfec:>\xfb\u067f\x1e1s\xb3\xdd\xee\xdc\xf5\xd2K/e\xda\xecL\x93M\x84\xc5\xc5E\xdc}\xf7\xdd\u0631c7\xfa\xfd\xbe'\xe5\xfb\xcf\u007fH)\xb1\xba\xba\x82W^9\xe3\xe1\\\xf6\xa1\xa1\xe9\xe9\xe9daa^KY4\xac\xe8t:H\xe3\xed\xb6S#7\xa7Y\x18\a\x0e\x1c\xc0\xae\u077b*Y\x96\xf4\x03\x95\x8c\xfa\xe8wW*\u22f6\x9c\xa1D\x85p8\xab\xca-\x18X\x0f\x0f\x998\xe5CR\f]`\xd7=M\xab\xdd1)P\x01Z\xab\xd4*\\\xcd%s\xd5u\xe4\xb1\x19T\xb5\xb3\x98D\x85\xa0\x94\xfa\xe4I\xf7\x84W\xb9\xca$\xe7\u01b3\xa6\xb03\r3\"\x9f&\x9d\u073b\xad\x18\x10\xaa\u0715x\x8f\x13\xe4\xd3\xe4[\x97\xf1\x13\xa2\xf1\xaa\xa2\aS\xed{A*\xca\\\x99\x15\x9aSE\x15\xd4\x11\xd1\x18;\x97[\xafsa\xc6K\x1b\x06\x84Dcz\x11B\x05\u0678?\xdc\xf0\x143 U\xc0k\x1b\x9bX>\xfd\xca5\u033c\x04\x00\x0f=\xf4\xc0e0\xffQs\xb3\xcf<\xf3\xd4\xe2\xf2\xf2\xf2\xf5\xa9\x17K\xd97\xfb\x86\x1b\xae\xc7-\xb7\xbci\x9b\x9e\xbf\xc4\xea\xea*.:\x83-\xf6R)\xa2(\xc4\xe6\xe6\u01a3\xccx\xb1^\xaf;\x89o\u0299\x0f\x91$\xf16\xae\xcc\rv\xef\u078d\x03\xfb\xf7W\\w~\x1d:\x1e`\xd0]\xf7\xb24=\xdeSL\x029_\xcb]\xaa{9\xa5S\\\x95\x97\xd2'\xee\xef\xb6\x1a\xf6\xb7\xff\xec\xe5\x97Va\xdb\xe4\x99\xcfJ\xe8\xa5\xf2\xa2S\x04\xc1\xf2d%\xf1\xf8\xba\xc0\xd9uQ\xe15K\u007fF8n\xc0\xf2\xc8\xc8\x1d\x1eK\xe1\x12\x82\x91}\u007f\xe2\nI\x84KxB\x8e\u007f\x9f\xca\xc0?\xb9M\\\xa4\u04cb\xa4\x13\x97h\xa4\ft\x99KE}\xc9*\xc0\xf5\v\xb2\xc1*\xf7\x021sa\xea\x17\xdeD4;\x9bg\xf6\u04d1\xdc{\x94\xba\x826\xa6\x16 \x83\xc8\xd9L[\xf9\xadt\xf2[)\x15u{\x03\xb4\xdb\xed\xbd\xdf\xf8\xc6W\xdf\f\x00\x0f<\xf0=z\xfe\xf9g.\x83\xf9\x8f\x92o\xfe\xeew\x1f\xb8\xa2\xdf\xef\xdf<\x1c\x8e\n\x14\x8b}\x13$n\xbf\xfdv\x1c9r\x04I2\u0716\u0dfe\xbe\x81\x8d\x8d\xf51\xc0SJ\xa1\xd3\xe9.\x03\xb8\x10\x04AA\x156\x18\f\x11\xc7\u0276\x05sc\f\x84\bp\xe8\xf0!\x04a\xe8\xde/Q\u0612\x03\xc0h8@o\xf3b\xb6\x95\x1d\xa3A\xca3\xf4\xa8\xaa\U000386be5\xe34\xae0\u0784\x9f\x86\r\xd64\\fn\xc6+\xd3J~\x9b*P\x8bQ!\x8es~+\x13\b\x94\t\x8bU\xc1E\xdd\xef\xe4\x19/&\x8d\xb9\xe8\x1b\xe4-\x19\xe94#U\xbdp\x15\xd5\xebX5=\x19\xc1+\u007f\x80\xb6\xdaHU\xf6\x19J\xa6\xf3\x85]VYic\r\xc1\x8c$\xbb\x18g\x93\xab\xe9\x1f\u075bm\xf2\xd7\xc3.\fn_\x94\xe9\xd1}\xab\x02\xcf7\x97\t\xf5\xc6\x1c\xa4T\xf9\u0535\x90Y\x1c\xa3\x10\x02Z\x1b6\x9aw<\xfa\xe8\xa37\x02@\xb3\u0660\xab\xaf~\xe3e0\xffQ\x1e\x17/\xae\xdc\xf1\xd2K/\x15\x12m\xd2\xcaw\xff\xfe}x\xeb[\xef\x84R!\x92d{\x82\xdf\xc6\xc6FiR2o\x82\xee\u0631C\xd6\xebuYV\xdf\f\x06\x03\f\x062\b\u0272\x00\x00 \x00IDAT\x83mE\x19U\xed\x9a\x0e\x1f>\x84\u9a69\xb1\x0fl\xba\xd8&\xc3.:\xeb\xe7,\xb0\b\x0f\\\x04\xc1\x10U\x83\xdd\x16\xdc6y\xab\xa1\x9f\x9eC\x1e\xc0g\x14E\x95\x05,\x97\xaa\xec-\xe9\x0f\xae`\x87h\xac\x06\xcf]B\x18[\xea\xd4y\x12oQE\xcf\xe4\x8dP\xcaV?\xf7{\xd3T\xab\u0523\x9e\x8a_\xd5\x05\xb7G@\xf3$\x90/7\xa7/\xd1[(}\xffR\x8f\x18_\x84\xb9\xb0\xf8\xb0\xb0\xbb5#\xc9\xd2G\xde\xee\x80Y\xdbIO\x14\x15\xe5\xe9k@yW\xd4\xfa\xec\xa7\xf3\xfeY\xc5\x0e\xd4[s\x90\xd2N;[\x03/k\xd6k\\\xffA\xca \xd9\xd8l\xe3\u0631\xe3\xd7\x01\xc0\xaf\xfd\xda\u007fc\xb6\x1b\xc5\xf9\xba\x04\xf3\x17^x6\x05\x85\xc6\xf9\xf3\xe7\xefX]]-LC\xa6\xc7\xcd7\xbf\tw\xdcq'\x00\xb3m\xab\xd8N\xa7\x83^\xaf\xe7\xed8\xd8\xf1\xe9\n\xbbw\xef\x0e\xa2(\x94eJ\xa5\xdb\xedf\xfc\xffv\xbc\xa1\xd2s\u06b3g/Z\xadVa'\x95]\xa7\x10\x88G=\xf46\xce\xe7\x8a\v\u05c02\xe9\xcc5Uh\xb1\xc9\x17\xf6Q>8\xefeQ\x92\xcb\xde\x1c\xab&\x9dv9\xe5\xd0s\x19\x1bc\xa2\xeb\n\xf3\x18\xc8\xfa\x93\x9dE\xb8\u326al\xa2\t\xca\xf2R(\x84O@\x8c\xd7\xc3\xf9.B\xf8\u050a3\x912 (E\b\x03\x81P\xd8\xec\x8b\xc9q\xa0<q\xb1\u068aIz\x8d\xfb\xe71*\x87'\xf9\xed\xa0\x9a\xb11\x04hE\xd0\xd2\x02:$\x81\x94\u02f5ufY\u033e\x04\xd4\xf3\xdaO\xd5+\xee\x9e4\x99\xaf>r\xa0vE`\xad\xb5\x00\x19\xd4\xc0Z\xc3\xc0\a~[\xf1\x87a(\xce_\xb8\x88\u04e7\xcf\x1ca\xe6\x03\x00\xf0\xb9\xcf\xfd\xad\xb8\f\xe6?\xe4\xf1g\u007f\xf6g\x00\x80\xfb\xef\xbfof}}\xfdm\xa3\xd1(\x03\xf2\xb4\x8am6\x9bx\xe7;\u07ce\x9d;w#I\xe2m\xfb\x02\xf7\xfb=\xc4qR\u079br\xad\x16A)\xb9<\x1c\x0e/R\xe6\xe4fo\u0635\xb5ulll\xa2h|\xb5\xfd\xc0|ii\x11\xadV\xb3\xa2\xbe\xb4\n\t\x93\xc4\xe8\xb5/ \x89\a\xce\x1a\x18\xa5\x00\x1f\xb6\x91k4\x89\x06\xc8a\x8f\xfd\xea\xab\x04&\\\x1a{\x1c\x93\xe5y\xcd4\xa6I\xac\x82\xaf\x14/\u007f\x8b\x8b\xe7\xbc\x05A\xe1\xab\xce\v\xc3<\x15\x89B\xd5\xc4\xc6\xf8\xa4,\x180\x12\x90M\x89\xa8\xa5\x10\x86@ \tJ\xe6>\xf4\xc4\x15\r\u05can$\x8fq\xe2\x97F\xf7\\\xc7R\x150W\xfe\x15\xc5&\x01c2{\xc5\x00H\x11\u0114\x84lI\x04\x01!\n\b\xb5@ \f\x04TV\xa1;\xddb\xd5$+\xe5\x9c:\x17\x9a\xb1\x949*\x02@\x106\x11\u0567\xb3\u0140\xa4\xcc\x1dH\xad\xfcQ\f\x87#lln^\xfb\u007f\xfc\x9f\x9f~\x03\x00\xbcp\xf4\xe8e0\xffa\x8f\xdf\xfe\xed\xdf\x01\x00\x9c={\u6593'O-\xd9!\x1a\xce\xfcM\x00\xe0\xdak\xaf\xc5\a>pw\xc6\xe1nW\u03bf\xdd\xeed&[)\bZ\x1b\xd9&N\x9dZ~\xe4\u0529S/*\x15xJ\x17`mm-\x1b\xe9\xdf\u0395\xf9\x8e\x1d;0==3\x0emi\xf1\u010c\xc1\xe6\n\x86\xed5\xab\x15N\xa9\x11m\xdcT \x95\xfa\x88\xc5\xe4\xf7\xb1\xd6${\x9c\xabo\x8d\x9am\u0569dKR\x04\x95,6\x8f\xb6\xc6\xcf\u025c\x0f\x15\x95\x13\x19\x85K\x15\x04L\xe9\x19y<gt\"xzC6 \xa7ZiJD{\"\x04\x8b!d$A\x82\x90Z\xe8\xb3\u007f}\x19h\x93w\x05v1\xb41u<A;8\x89\x17\xa1\t\xec\x17c\x12AUY\xecsi\xffAn\x97Q\x13\x88\xe6\x034\x96B4[\x01\x1a\x8a\x10* \f\x05\x02E\x90\x82\xb2\x1dV\xb6\xa0W%\xfae~\xff9\xfd\x92\ue10d1 \xa1\u041c\xd9\xe1b\x13\x01!\x03{O\xe6\x13\xd9\xc4$x\xb3\u075ey\uaa67n\x06\x80\u007f\xfb\x87\u007ft\x99f\xf9Q\x1dG\x8f\xbex\xd7\xf2\xf2\xa9\xac\xf2\xf6md\xef\xb9\xe7\xa7q\xed\xb57\x809\u0656\xf2=!\x04\x8c\x19as\xb3\x8dr\xf043s\xb3\xd9\xc4\xec\xect\u007fccs\x98\xe7n\xd2\x04\x9e}{\x82\xf9\xdc\xdc\x1c\x16\x16\xe6|[u\xf7}\x93\x01\u06f0\xbb\x81A{\x1d\x82\xf3\xc6V:\xaa]\xfc\xca\x05\u02570\x85\xf4>\xacN\xf5\xe0\x9agF9n\x9e\xaa\xc2\xe0\x90\xed\x02\nU$\x97\x99\x80\x8am\x02\x95\x1a\x9e\xa5\tN\x06\x17\n\u0182\xbb\x1f<\x1e\xa6b\xa2t\\UB\x85\x06\xb1\x0e\bIK\"\xdc\x11!\x9c\r f$h1\x80\x98\x92\x10\x81\x00I\v\xeae\x0e=\xd5a\x17TC\\E\xdb\xd3\x18\u060eW\xdf\xc5\xf0\x16\xe6I\xb2E\x14'@\xa9\x94?\x97>\x8b\x80\xa3\x8b\bAK!\x9cRv\xc71\xab C\x01!\t\" \u021a\x80t\xa9D\xcc\xc5\x1d\x12\xfbrt\xdf\n\xdf\x05\x84\xa3\xe0\x9eh d\x80\u9e7d\x10$a\xb4v\x01\xebA\xf6\x9c\x86\x19R\x05|\xe6\xcc9\xf4\xba\xbd\x9fb\xe6\xc5S'N$\x97\xc1\xfc\x878^~\xf9X\xf6\xe7S\xa7\x96\xdf\xea\xab:\xd2\n|\u07fe}\xf8\u065f\xfd\x10\x00\x8c\x01\xe5v\xaa\xccG\xa3\x18\x9dN\xb7\xb0s \u05d4i4\x1a8t\xe8p\x90$\x894.\x9d&\xa5Y:\x9dv68\xb4}\x9b0\x8c0\f\xb1\xb8\xb8\b%U\x11<\xbd\xff\x8d\xfam\f;k\x99\u01ca\xc8\x1a\x96\\P5\x14E-\x13\\\x14\xab\xceB\x10\x92\x9aDR\xb7\xde\xd8,\x8b\xcd@\x1f\x18\x99\xa80\x0e\xee+\u034b\xd4CU\x1d\u0293Kx\xf67\xf6\x1e\xd7N\xbeR\x851\xa9\xf6\xcd\x11\xa9\x88\x8a\x86\x80$\x10\b\xe7\x034g\x94\u0376\x04A\xb4\x14\u010e\x104\xab\x104\x04\xa2\x86DX#\xc8\xc0\xaa3\xca\x16\x88\\-\xb6\xc1\x98\x82\xe7UD\xe4\xb1'\v\xe2rhK\xc5N\xa6J\x97.\xa5@(\t\"\x14\x10S\n*\x10v\r\xae\vPS:\x87D\x86P\x04\n\x90\xc9\x14\x99`\x1b\xe7\xee\x1a\xb3\xf5?\xf3\ua9ca\u05d6\xc0\xc6@H\x89\xa9\xf9\u0750*\x84\x8e\x87\x10BB\x85\xb5\x8c\x8f'\x10\xa4\x90\xd4\xeb\rq\xf1\xe2\u0291o~\xf3k\xfb\xf1:;\xb6\x1d\x98\x1f:t\x85\x03\xb4\x8d#\x17/^\xbc15\xa0\xf2\x03\x9c\xef\xba\xeb\u0778\xf1\xc6\x1b\xb7-\xc5\x02X\xbf\x98\xd1h\x84^\xaf[Y\xed\xd4\xebu\xec\u06b5\xdb-F\xc5\b\xba~\u007f\x80~\u007f\xb0\xcdo\x1d\x03\xa5\x14v\xed\xdam\u524c\x92\x0e\x98A$0\xe8m\xa0\xbfy\x01\u0485Xg\xfc\xae\xa3A}\x8e=W`O\xe0\xae=\xe0Kk\\\xa9\b\xc1\x9cB\xb8+B4-\x11\x04\xae\x9a\x93\u00abP=m\xbb(\xf3\u9503\x00y\x89N\x15\xa0\xc7>\xe0\x15x\x96-p\xb24\tC%v\x19%\x83)\xbf\xfa\u0512\x10\xce(L\xcd\a\x90\x8a\xb2\xea\x94\x00P]\x82\x16C\xc8\xf9\x10\xb2%\xa1\x9a\x12a3\x05v\x01\xa1\xc4Xq\xfc\xc3\x05\x9e\xd2$W\u0742\xb4r\xac\xd7P\x9eG\x12\x84@\x00R\xa6\\\xb9\xb0k\x9e\x81m~\xb6\xa4\xbd6W\xd8hg>f\xdf?\x02K\xf7\xe5/Zd\x1b\xa6\xe9i\xd8\xde\x1a\xe7\n8m@$\u0418^B\x105a\x92\x11\x88\x04\x82\xb0\x9e5p]\u07ca\x12m\x10\xc7\u0261\xfb\xee\xfb\xf6^\x00\xf8\x8b\xbf\xf83\xba\f\xe6?\xe4q\xff\xfd\xdf}\xfb\u0673\xe7\x9a\xddn\u01fd9:\x03\u024f|\xe4\xefA\xa9\bI2\u07364\x84\xb5\x8b\xd5^s\x96\xb2b\xcd\x18\u00edV\v\xfb\xf7\xef\rF\xa3xl\ua0d91\x1c\x0e\v\x00\xbf=\xafQb\u03de\u0748\xc20\xf3\xd1@\x01\x9f%F\x83\x0ez\x9d\x15\xa7C/\x19\xb2O\xf2*\xa9\xa0_\v\x01\ri\xb8\x80\"\u0526\x14\x9a\xf3!\xa6f\x034\x97B\xd4\xe6\x03\x845\t)\x01#,\x18he\xbf\xd8\xe3\u05ab\x16\x86q=J\x95\xab\xa0w\x19c\xdb\u007f\x06ck\xba\x82_\x05l\x02\xb6*W-\x85\xd6R\b\x15\n\x97\x1f\xee\xe9\xaa\rCD\x02b.\x80\x98Q\x10\x91\x80T\x02A] \xaaK\u051b\x12Q]Zj\xa9\\\xa5W\xf6\x05\xb8\xe2\xef[xLr\xb1/Q\xb4\xdf\xe2\xca>\n\x13 \x05!\x10\x04D\x041\x13\x80\x94p\x13\xacNG\x1e\x120\xa3@\x91\vr\x16v\xc7\x01o\xc7e$`\x04\x175\xf5Y\x95^\xbc\fv1zF3\xc2h\n\u0359\x9d0\xc6\xc0h\x9di\xcd}\a\xc6 \bp\xf4\xa5\x97\xf0\u02993\xb7\x00\xc0G>\xf2K|\x19\xcc\u007f\xc8\xe3\xf1\xc7\x1f\u007f\xc3\xfa\xfa:\xe28v\xa1\u01f6\x8c\xbb\xf9\xe6\x9bp\xd3M7\xd9U[\xebm\xfd\xe2\xa6\x15\x02{\x83\x1f\xcc\xe0(\x8a\xd4\xc6\xc6F\xbb^o<U\xaf\xd7\xeby8q\x0e\x18v\xa4\x1f\xdbzp\b \xec\u06b5\v\xb5ZTIK\x10Y\xb3\xad~g\xd5\xf3\xb2\x9b\\\rV+\xe9r.8\x93A3AJB\u0610\b\xe7\x15\u009a\x80\x02C\xd5$\x82\xc5\x10\xe1B\x80\xa0!\x01E\xd0d+\xdcT\xbfl$\x81\x95\x03t\x81\x92/I\x85Sb\xe5_\u02a1\x11\xb4%{\x81\n\u00a6\x9c\x0f\xe1\xafq\x9a\x19\x14I4\x96B\xa8\xa6\xcc\xf3;\xcb\u02dea\x90\x02\u4302\x98\x0f \x1a\"+\x18d@\x88\xea\x02\xb5\x86\x84\x94\xe4\x16\x03L\x1e&\xe2\xf2\x8b<\u10b8\xb8\x1c\xb1G\x92\xe5\xeb\xf3x\x9b\x97\t\x10d\xabr\x92\xb0TQCT\x8e\xfdS$\x80)\x05\x04\x04\tF\x10\x10T\xe8\x02*\\C\xd4\b\xbb\xe0\xa5\xd6\r\xf9g%\xd7\xe1\v\xca-\x8cu\x92@\x85u\xcc\xed<\x04!\xadlV\b\u5a16|GDRbmm\x13Z\x9b_4\xc9p\x0f\x00<\xf4\u0403\x97\xc1\xfc\xb5\x1e\x8f=\xf6\xb0\af\xed;\x8c\xd1.?\xd3d\xc0\xfd\x9e\xf7\xdc\xe5\u0329\f^\x8f\a3\x9b0\x8c\xb0\xb6\xb6\xfer\xb3\xd9z\xa9\u0468\x85y\xc0s\xfe\xb8^\xaf\ufa0a\xed\f\xe6\xc0\xee\u077b\xd1l6\xbd\x81\x0e\xff\xb3N\xd0I\x8c\xee\xfay\x8c\x06]\x90\x90@\xc9$v\f.x\x1c$\x8b5!AH jH\x043\n\xa2)\v\xce|\xa4\br.@8\x1f j*\xa8\xd0\xf2\xb1ieh$A\a6\\\xd9\u0212\xf7\u01d8\x81k9\xad~\x92\x94\xe2U\xf4P\xbc\x85)\x83?.\xf1\xf1)\xa8\x93\xa5\x8e\x82Y\xe5R\x88\x8a\x96\x01\xec{\x95\xbb\x1c4j)\xd0|\x00jH\x90\xcc\x01;\xacI\x84\r\t\x11\x12\x8c\vv.\xbe\u063e\xddm1:\x88\n\xb4\x10U\xd2]D\xe5\f\xd4j\xbf\x17\"@\t\xabNA$A\xd3\xca\n\xe5\xb9\xc2!\x8c\x004$\xa8!!$ \x89!%\x01\x8a`\xbckO\x9b\xbb\u0185\xd1d\xbaq\x1e\xff\xddl\x12\xa8\xa0\x8e\x99\xf9\x03\x90*@<\xe8B\xaa\x00a\xd4p\xe7m\a\x89\x88\x04\xb4a\x9c^>}\xc3'?\xf5\xe9]\x00p\xdbmw\\\x06\xf3\xd7z\xbc\xe9M\xb7f\u007f>~\xfc\xe5\xb9^\xaf_\b;\x06l\xfed\xad\xd6D\x1c\x8f\xf0z>\x1a\x8d\xba\xba\xf2\xcaCJJU9\xe9ii\x16\xb3\xadi\x16\x00\u0635k\x17\xe6\xe6\xe6\x9cE)\x8d}\x98\xd9\x18\xb4WO\xa3\xbf\xb9\x02!TA\a\\\xa5N\xe6\x02\x90\x97\xab}\a\xe4u\x89\xa0\xa5 f\x02@Z\xc9c\x01\x9f\x14AN+\xd4\xe6\x034[\x12\xb5P \x90\xb6R\xcb\xf8h%\xa0C\x01\xdej\xf2\x06\x93H\xe2\x8a\xc7l\xe5\x9a[\xad\xad\x19_\xcel\xe61DC\"\x98\x0f,(\x1b*\x00\xb9\xf103[z\xd8y\xbd\xd6$h!\x00MI;x\xe3\x1e\xa4\x02BT\x97P\x01\xc0i\x8a=\xa1\xdc\xe4\x98@\x00q\xd5*\x9b\xe3n6\xfcU\xa5\x9e\xf7\x8c\x81\x89 \xa5}ohZ\x82\x1a\xa2\xba\xdf\xc0.\xf89\xe5\xcf#@\x10CR\xdab\xe5,z\x8e\x05\xdb0\x8f\xec\xb51\xee+U\xa8\x98\f\u05d95\x88\x14\x9a3\xbb\x11\xd5g\x90$#\x00\x04)\xc3,\xfc\x82\\\x9f\aDx\xf9\xc4I\xb4;\x9d\xd7U\x94\u0736\xa4Y\x8e\x1e}\xf6g\xd6\xd7\xd7\x0f\xb6\xdbm(\xa52\x8a\xe5\x96[n\xd9\xf6\x8d\u03f1\x8a\x8c\xaa\xff-\bB\x04A\x1dJI7\xe9\t\xf8\x01\x15\x9dN\a\xa3\xd1h\u06cf\xf4\xcf\xce\xce\xe0\xe0\xc1\x83PJ\xa0<\xff\u01ce\x1f\xd8X9\x89\xf6\xdaiH\x15\x8cU\xabT\xfe \x13\x15\xb6\xee\x19^\x01 \xc1\b#\x81\xb0! f\x94U?\x94'@]\u04cf\x14A\xcd\x06\b\xe7C\xd4\x1a\x12\x8dH\xa0\x1e\t\x04\x82\x9cc\x1f\xc3( \t\x85\x954z\xa4|\xbeo\x98\xe0\xa4H\x15Z\xeaI:\xed\x82+ O\xa0c\x9cZ\x8b\x192\"\xd4v\x84\bZ\xaa\x10f\\\x984\xa5\xaa6\xad\x03\xb9P\x00s\x01hV\x01\x91\xbd.A\x84@\x11\xa2\x86\xb4\xfc{\u5fa1\xca[\x86's\xe0\xe9>\xa3\xf0\x92P\xf5\xf3\t@)XuJS\xdas\x134Y\xaa\xee\x16(\x0e\x004\xad\xa2\x85Rq\xbfa\x10\xe7\x03D,a+\xfcBw\xc26=\xcb=\x11\xd61\xea\xad\x05\xcc,\x1e\x86\xd1\x1a\xc3A\a*\xaaC\xa9\xc8=.=s\x81\xd5\xd55\x1c\u007f\xf9\xe5w1st\x19\xcc\u007f\x88\xe3\x1b\xdf\xf8\xd6\xc2\xc6\xc6f=\xedF\xa7\x14\xcb\xdb\xde\xf6V\x1c<x`\xdb\xc5\xc2\xfd \x87\x94\xf6\xa5\xb7\x03CN\x99\xec\r\x0eu\xbb]\x8cF\xa3m\xedi\x0eX\x89\xe5UW]\x05\x95\x01u1\x1fS\b\x81\xf6\xda+\xd8\\[\x86\x10\xaa\xf2\x03L^eJ\x13\\>\x18\x80\x90\x02A$ \x9a\x12\xd4\x14\u0632\x1cf\xab\x83\x14-\t9\xa3 k\x02\xa1\"\xd4\x03B- \xfb\xf97\xae\x99\x16\x88\xdc\x0f\xa4\xb8ba\xe2\x84\xd1V&]\x99/9\x15(j\x1a{\\i\xb1\x92\x84p.@8\xa7\x1c54>y\xeaOb\xd2\xd8\xf9\xb0\xe5\u0195k\"\xce* \xccJW\xdbg\xa8K\u0210\nz\xf4I\x84?a\xf2\xda\u00d4S>\xbc\xc5N\x86\x85U\x1cII\xa0@\x80f\x03P]nyO\xa5.\x88\xd0\xda.H5\xe7\xa3b\t\x95\xac\x02O'?\r\x8c\x1d\xfb\xf7\x16\x1f\xbf\xa5\xcd\xc6\xc0\x18F\x12\x0f\x11\u05a60\xb3x\x10\x00c\xd0Y\x87\njPQ\x03\u0326d\x8fK\u8d3b?u\xfa\xe5\xa3!\x00|\xf3[_\xa7\xcb`\xfe\x03\x1cI\x92\u0726\x94\xac\xc7\xf1\xc8v\x9eMZ\x99\xbf\taX\xc7h\x14\xbf\x8e`\x9b*+\xf5\x14\u0327\xa6\xa6\x10\x04jLOnm\x00\xe2m[\x99\xdb\xddQ\x82(\x8ap\xf8\xf0!DQ*O\u0307P\xec5\t\f{\x1b\xd8\\}\x05Z\xfb\x1e4\x8cr\x13\x8d&#\xbdU5\x84\x04\x11\t\xa0\xa9\x00)&\xb3\x1f)mj\xec\x1d.\xa6\x14\xc4l\x00\x8a\x04\xa4\x04\xa2\x80P\x97\x84\xc0\r\xdah\t\x18\xe5\xa9\\\xbc\xc5e<\x1d\xf9\xd5|\xbb\x8a\xed\xa7\xc2w\xfc$\n\xbb\xeb\x00\xa2)\x85p)\xb4\xea\r\xe6\xca\n\x9a\xb6\xb8\xb7\xb2\xbf\xa7\u0297\x96\x02\xcd\x05v\a\xe3\xaaW)-\x8f.BQ\bw\xf0\x17\x84-]\x12\xdd\x0e\x8a\u0299\xae<~\x9eL\xee}\x13\xee\xf7OK\u0434\xac\xfa\x81\xf1\xb5\x80\x19Fk\v\xcbu\x01\xad\xa8\x90\x13j\xc1\xdd\x01\xbb\xb6`n\xd8@\x1b\xe3vC\xaer7\x9c\xed\xe4u\x12C\xca\x10\xd3s{\x10\x84\r\xc4\xf1\x00\xc6\r\x14\xf9\x94\x93\x10\x12q\xac\xf1\xfc\v/\x04\xdf}\xf8\xd1\xdb\x01\xe0]\uff0b/\x83\xf9\x0fp\xf4\xfb\x83}\xcc\x06I\x92p\n\xe4\x87\x0e\x1d\u0111#G&R\x17\u06cbZ\xb1\xa1\x12J)\x84aP\xf9\xfd\xd42vqq\x11a\x18\x8e\x81y\x1c'\u06deJ\xd2ZC\xca\x10\x87\x0e\x1dB\xb3\xd9\x04\x8d\u036b\xb8\x0f\x92N\xd0^[\u01b0\xbf\t\x92j\xecs<1<\xce\xeb5\x92\x84mf6%P\x17\x97\xd4L\x97{|4%!\xe7\x02\x88\x9a\xb4\xcd8E\xa8)\xb2L\x04;\xb3\xa7\xb0\u031f\xfb\x13G\xfe\t\xb9\xa6$U\xc78\xd3\x18\xdf_0\xcb\xf5*\\\xf7h\u05e8\x8c\x96\xacf<\xdd\x11\x14\x1d\x82+\xf25'\x82:\xf2\u0160)m5\\s\xce\\l\xd7\xc10\x12\xa0\x80,%?&\xe8\xe1\xb1\x19\xa6\xe2\n\x96n*\x8a*\xa0\u00a2E\x16]\xa4 (bP]B\xcc\a\xa0\x80r[\xe2\xca\xe7\xf7\x831\x9cQ\x96\x02\x92\x1a\x90\x10g\xcdN\x18\xb7+H)\x97\xb4]K9\xc5\xc7\xc6d\xbd\x01\xb2Sy`N\u041a\u074d\xe9\x85}`\x93`4\xe8@\x05a\x1e\xf8\xec\xceA\x1b\xc6\xda\xda:^|\xf1\xa5\xb7\\\xa6Y~\x88cuue4\x18\f\x01\x10\xa5\xc3BW^y%\xf6\xed\u06c7\xed\xdc\x14,V\xad\x06Q\x14\xa1\xd9lU\x9e/;\xbd\xe2\xd2\xd2R\x10E5\x18w\xa1\xe9c\xe38\xb6\x9e\x12\xdb\xf8Z}E\xcb\xc2\xc2B%\u0352n\xc6\u06eb\xaf`\xd0\u07c0P\n\x13`\xa2\x1a\xc5\x1df\xaa@@\u0525\xad6\xabT\x10\x18\xe7\x97\xcb\xcc\b\xb5,\xa0\u021a\x84 \x82\x14@\xa4\x04\xea\u03b0J+\xabt\xa9<1*k\xce\xc7\u03c1\xb6\xa0\x0e\u0199\xe8\x94\xcf\x06\u0080\x10\xcd)\x88Y\xe5=\xfc\u0485 OlV\xe6\u007f%fPC\x80\xe6\x02\x88\x86\x84P\x04\x99r\xe85\tR\x02\xc6\a\xeeB\x9a\x0f\x8d\xc5o\x94\u007f\x0f\x8f-d~\x8f\xc35=\x03\xf7\xfb\xeb\x12\xd0[Y9\x96^'\xf6<\xcc#\x01Q\x17\x10\xa2\xd8*\x17\xe9Tq:8$\x04\x84\xb7\x98\x12R\x99\xa2\x80 \x01\x9d\xc4h\xcc\xee\xc4\xcc\xd2!\x18\x9d`\xd8\u06c4TA\xd6\xcfI3D53\x12mp\xee\xdc\xf9w\xa5g\xf4\xf0\xc3\x0f^\x06\xf3\xd7z\f\x87\xa3l\xe23\xb7[\u074d\xa5\xa5\xc5m\x9e\xc0S\x04\xba0\f1==]:\xdf\u051a@\x13\x00Q\xafG\x17l\xd1P$mm\xdaP\xf2:\xb8V\xc6\xcc\xcc4\xf6\xef\xdf?^\x99{ \xb1\xb1z\n\xfd\u038a\xfd\xd0\xf0\xa5\xdcW\xbc\xa7 @*\x01U\x13\x10-\t\x8e\xc4%p\x8e'>+\x11\xac\xdcm.\x80\xacK\xeb\x01B@(\b\ra\xa9f\x1d\n\v\xe8\x84\n\xbd\xb8\x1f\r?I\x01R\x95Z49\xb8\"\b\b\xe1t\x00\xb1\x18\x82\x14Ys@\xc7Qp%\xe8\x95'-\xcb\xf9\xa5\x9e\u076f\xff\xad\xba\x80\x98U\x10N\xbah\x9b\xa2@X\xb3>(LEs\xafI\xec\xc7\xd8N\x83\x8a\x8b({\u0222\x84m\xbe\u04b4\x82\x9cQ\x15\xd4\u04c4EW\x00`\xe3\xeck\xed\xffCEh\xce(4Z\x12\xf5\x10\b$l#\xd4x\x80\x9f\xf2\xf8i\xc2\x14\x18\x865\x8cvT-3\x8cI\xa0\xa2\x06\xa6\x16\x0e@\x86u\x8cF}h\x9dX\xd9,\t/\xb1Np\x1c'X^^~#3\xd7\x00\xe0\xd6[\xef\xb8\f\xe6\xaf\xf5\x90Rf>%)\x98OOO\xa3^o\xbc.T,)\xcdB\xa4077\x8b \b\xe0W\u0756\xa20\xd8\xd8X\t\x9e\u007f\xfe\xe8\x97766\u06f5Z\xbd\x10H;\x18\f\u0728\xff\xf6\x06s\xadc\xcc\xce\xce\xe2\u0211\xab\xac?\x06\xa1\u020b;:\xa2\xbd\xf6\n:\x9d\xf3v\xab[\xa60\xaa\x02\x9d\xddC\x94\x04TD\x90-\t\x9aRn\xc2o\x82\xcf\t\u00c5\xf5\xa2Z\x80\x91\xfe[\u04daU\u0256\xb2\n\vE\b\x94@]Y\xd3*\x1d\t\x98\xc0sW\xf49\x9b\t\xa1<\x05m\xfa%\x17\x18\x02\t L\x1b\x92\x8b\x01\xe0\x86\x83\xf2\x11\xfc\xaa&g\xfe/\x95\xfd\x05\x1e\xcf\x02%\xa2|\u0769\t\xd0|\x00L\u0641*!\xad7z\xbdf-g\x89\xc6s83\x9f\u022cb/\xee\b\xd2\xde\x04yzI\u06ec&H)\xecnh)\x80\b\xa8b\xc7Q\xd4\xed3\xf2\xb8?\xd6\x1aF'\x96\x0f7\x06\x82\x18A\x04\xa8\x96@P\x13\x88\x027\x80\x94\x017\xf2&\xa9\xf1\x16\x027=\x9e\xfa\x9a\x83\x01\x9d\u0118\xddq\x18\xd3\xf3{\x91\x8c\xfa\x18\r{\x102\x80H\xc7G\x89\xa0\x94\xa2N\xb7\x87\x13'OM\u007f\xfb\xdb\u07f8\xe72\xcd\xf2\x03\x1e\x8dF\x1dR\xca\xd2M)\xb6=\xb0\x95h\x14\x00\xc0\xcc\u0334m\x0e\x96\x8e$\xd1t\xe1\xc2*\x1a\x8d\xc6@\b\x91\x94+\xf0tXj\xbb_s\x92h4\x1a\r\xec\u06f7\xcf)Z\xaag8G\xc3\x1e\xba\xed\x8b\xd0&\xa9l\x82\x8e\xcdQ\x12 \x89\xa0\xa4\xb0\xb4\u0234\x0f\b\xd5\xceQ\xa9\xcd\x15\xc0\x93\xeb\xf3\xf4W\xd6\x04h^\xd9\xe6\xa8\x04 \xac?xMX\xa0M\x02\x01\x13\x8a|\xb0(\xe5^\xb7|?\xe8U\xbc[\xf6\xf9\x02A\b#\x011\xa7\x80i\x8fz\xe2\\\xbf]\xad+a\xcfY\xb2\xaa\x84\x9eddnA\x17\x01\x81f\x953\xb4\xb2^)2\x14\x88\"\xfb%E\x89P\xa9Xo\xa9\xfc_\u02af\x9d\t\x80$(a\x9b\xd5b1\x80\xac\x8b\t@>.+\x05\xb9f\xa5N\xc0\u0680\x8dv\x92D\a\xc6\x01@!A(\x810\x90P\x822\xee<S\xbf\x19\xed*\xf1\xb4?\x95\x1a\u0739\xe7\xd7#L\xcf\xed\xc5\xcc\xc2\x01\xb01\xd0\xf10\xf54\xcf_b\x12\xd0\u06a0\xd3\u9d3e\xfd\xed\xef\u0718\xf7\xb2\x06\x97\xc1\xfc\xb5\x1csssQ\x14E`\xce;%\xb6J}\xfdM}\xb6Z-\xd4\xeb\xf5\xe2N\xf8\xffc\xef\u0363,\xbb\xca\xfb\xd0\u07f7\xf7>w\xa8[Swuu\xf5(uK-\xa9\u0552\x00\v4\xa0\x81Qf\xe6a<\x80\x00\x93x9\xb1\x8d\x89\x87\x84\x10\x9c\u007f\xf2\xfe\xc9K\x9c\xbc8\xce\xf3\xb2\r\xbc\xb0\x1c\x9c\tX\xd8\x01\x83\x9e\x102\xc6\x0fd\x03\x92h\t\xa3yhuK=O\xd5\xd55\xdc\u1733\xf7\xfe\xde\x1f{8\xfb\u073a%!/\xbf\u040a\xea\xacURu\u056d{\xcf\x1d\u03b7\xbf\xfd\xfb~\x83\xff\xc8\x14E\x81}\xfb\xae\xcc\xda\xed\xb6\x1c\xee\u078a\xa2\x80\xd6\xe6\x82/\xe6\xd6\x1a(\xd5\u0116-[\xd0h40\xdam\x10`]\xa2\u05dd\x87\xe6\xd2\x05\x03\xd4:\xb3a\xba\x9f\u00f33\tg\xf3:\xa9\xa2T\xbd\xa6\xeec\x1a\x01\x05$\xf0\x04\x0fA\xde4T\xd8\x1a\x02\xb4A\x81&\x14D\xe6.\xfaL\x12\x1a\xbe\xa0[E\xb0\xa9RT8\xb5\xe1\xaa::T\xf0h\xcd e\x1f\x94 \b\rE\x10\x93\n4S\xc1+\xa9\x00sT\x02Sb\t\x81\xe7\x97 \r\x95\xfa\xa1\xe7N\xbe\xa0cR9\x11\x8fpP\x8bj8\ua9d4U\x90G\x88s\x1b\xbe\xb3\xe1\xfdA\xb4i!o\xa2\x95\x11\xc4F\x055\x95\xd5\xf8\xe8\xa3\xf1\x9b\xfaN.v\xd26\xd0\x0f]\x97\rk\x01\xc1\xe0&9\xda)\xac\x0f\xe7\xf0\xcc\x16k\xc1\xda8.zl\xaal\xfc^\x04\xd8\xc72\x94ja\xc3\xe6K\xd1lO\xc0\xe8\x1c\xd6j@\u0238xy\xb1\"\x17E\x81\xe3\u01cf\xbf=\x9cn\x96\xb5\u058b\xf9\x8b:)!\x9e\xb1\xd6B)E\xa1\x8b[ZZB\xbf\xdf_\u0571_\xe8G\xbb=\x86V\xab=\xf2w\x83A\x1f7\xdexC699!\x86#\xf1*\x98\xe5\u009f\r\x00\xc0\xec\xec,:\x9d\xb1\xd5\xe5\xc5\xeb\u05ad5\xc8{K0\\\x823\x91fP\xa0\x96\xa4\xe9\x8bA&\x9d\x9d+\x8fI\xe7\xd3!B\x9e\xa3\xaf2\xc4\xd1pkTM\x8dl\x88U\xbfO~`\x01(\xe1:\xd5\t7\f$\u12ad\xf4\xf7a\x15\xc14\xbdR\x14\x14\xdd\xfcj@|(n\xabL\xc4\xea\x10\x05\x93\x83\x1f2E\x0e\xbb\x9ei8\x0e\xb5\xad\xa0\x82\xf8\xf4j\u0557Gw\xe8#\x81\x9e\xbai\x18Q\x8a\x87$\v\x99\"\xc7r\x99T\x80r;\x13\xca\x04dS\"k\b(\x95\xb4\xca\u00b3n\u05b0\b\x8e*K_\x04\x95$\u0229\fjc\x96\u030d+\xc6Im\xf1\x1e\xe6wz~9\x9b\n/\xaf \x12\xcfb\x91N\xf0\xc5`\bb\bI1g6\xae[\"\x9d\u06cc\xb04\xb6%f\xb6^\x8e\xf1\xa99\xe82\x875\xdaw\xe6\x14\xbby\xa5\x14\x9d=s\x06\x8f<\xf2\xe8\xe6s\xe7\u03bcj\x1df\xf9[\x1cJ\xa9\xfd\xd6r\xb7\u0468\xe0\x89\x03\a\x0e\xe0\u0529S\x90\xb2\xf1\x92Q\u007f\xbab\xdeF\xab\xd5J0\xf3\xc0\xf7\x15$\x84\x90;w\xeez\xaa\xd9l\x9ck6\x9b\xb5\u03bd\xdf\x1f\xa0,_:|\xfa\xa9\xa9)l\u0738\xd1C\xe4\xc3v\x87\x04f\x8b\xa2\xbf\fkJgO+\x87\x82\x13\"F\ue2dd$\xa0)\xc0S\xd2\tG,\xeav\xe2UtP\x1a(\x14\xbb;Ak\x80\x1ea\u06ddt\x82\x94\t\xd0T\x06\x9aR\x10\x99\x80\xf4\vJ\xac;\x020\x99\xa8V\t\x1a\u00a91\xdc\xfe\xf3PG\x8dJ\xb4\xd3$\u020e\xb3\xafE\xa0!bDm\xe3\x90\xda1\\\xbbG\x81/\xc3\x05\x96F\x92\x06k\x1d\xb4u\"%\x9a\x92\xa0\xa9\f\u021c\xf1\x18Ig\xd2\xd5P\x02*]O\x12\x16\xce(\x83\xe2`Q+$ \xc7\xdd\x1c\x802\xb1\xea\xbcG\xa6\x15%\xef_eN\x97\x0e@\x1d\xccb=\x06\x0e\x82\xa3Z*\xb7\x83\x12\xd2\xef.\x84\xa3\xfc\x92W\x11\xdb@]\xf4CR\xf6n\x89\xe1\xff\x9d\xa99L\xcf\xee\x82\x10\x12V;C?!\xbd\x93\xa2\xef6\xba\xbd>\x16\x16\xce\xed\xfc\xecg?{C\n\x9f\xae\x17\xf3\x1f\xf1\xb8\xed\xb67\xcf\xcf\xcdm6Y\u0588\xea\xee\xfb\xef\xff>\xee\xb8\xe3N7\xc7i\xb5\xf0RI\xceN\x8byz\xd5\n!!\xa5\x90Dt\xa8\xd5j\x9d\xe8t\xc6\xfds\xaa\x02*\xf2\xfc\xc2W\xba\x06\xf5\xea\u018d\x1b\xb0m\u06f6h\xff;b\x88\x80\xa2\xbf\f\xa3K\x10I\xb7}W\xae\x80\x84\xad\xbc\x94\x84,#\bE\x80\x02xR\x82\xc7%\xb4e\x94\xb6N\x88s]\xa7+x\x14\xc1Z\xae\x93\xe6\x88kt\xb9\x14\xaap\x85\xb2\x12\u0650\xf2l\x8f\r\x95Z\xb4\xa5\xc8\xd58v\xe7g2\xaa\x02.j\v\x11\xd7\xf0c\x1e^\xc8\xe0\ng\xd6\x12\x90\xbe#\xa7\r^\xa8bQs.X\x9bQB\xabb\xa9y\xd5\"B\u03c3\xdb\xd7=^\x9c\xe4\x9da\x05\x81'=\u44b9\xe2H\x19\x81\u048cQN\x92\x93\u05a2\xb9{\x18J\x8dI\xa8M\x19\u460c\x8b\x00%\xbb\xa2\x10\xc2<\u0681\u04a7\x18\xd9\xca_\x98\r\x83\xad\x89E\x18\x81?.\x1d]\x91}\xe83\xc8\u07f7\xf7\xe7t\x8fc\x9cR\x14U~\xb0\xf1\"Dk4\x88$6_\xf4\n4\u06d3\xd0e\xee\xc2S\x84\x02\x91\xf0<u\x03kM\xd9\xedv\xc5C\x0f=r]\xf8\xbc_\xa8\xb5\xe7\x82,\xe6\x97\\r\u065f\xef\u0739\xf3\xc0\xcc\xcc\f\x88D\x84Z\xfe\u077f\xfb\x1d\xdcq\xc7W D\x86V\xabs\xc1\xa6\u05e7E\xae\xd3\x19[\x05?\x00\x801\x1a\xa7O\x9f\x06\x00\\z\xe9\x9ef\xa7\u04c11&\x8a\U000965d7\xd1\xef\xf7_\x12\xc5\xdcZ\x8d\xa9\xa9il\u077a\xb5\x96\xbf8l@\x95w\x17at\x0e\x9fs\xe6c\u037c\xf22#\u0226p\u0635$P\u01e96\xa1\x04r\xcd\u022d3\x99\xa2Z\xe5\xa3U\x1d0\x8d\xe8\xc4G\x03\xb5\xc1X\xa9bP\x10\x11\xc4d\x06\xb91CcL\xa2\x99\t\xb4\x94@[\t\xb4\xa4[dB\x92\x11\x10\n\x1c\xd5pn\x1e\x02\xa9C!WmW\xc8\u0174\xf7M\x11p\x1dc\x12T\xbd\xe6\xc1\xb4\x1a\x03\x1f\x82\u04c7\xbbt^\x13\x9f\xf63(\xcb\xd0\x1e\x86f\x01\u0414r\xb0KS:\xb8B\xf9\xf7A\f\xfd\xe1H \xdeY\xd2f\r\x81\xc6\xc6\x06\u0114JnZ\xa1\xeb6\xc9\xf2\xe0\x98\x182t\u007f\xb1\x88\x06\x88\xc5\xd4 \x96\xf8\x05\x06+g\xb6\xe5^\"\u007fG\x89L\x94@\x10\x95\xa5fT\xa3\x8a0\re\x8b\r\xdb\xf6`|\xe3\xb6\xf8\xb8\xe13\xc3^\xb0\xa4\x94\xa4\xb3g\u6c7c\xbcr\x033_\x06\x00\xdf\xfd\xee_\xd3z1\u007f\x81\xe3\x87?|0\x14\b\xbe\xee\xba\xd7|a\xeb\xd6-\x18\x1f\x1f\x8f*\xca#G\x8e\xe0\x1f\xfc\x83_\u00a7>\xf5\x87\xe8v\x97\x90e-\xb4Z\x9d\x18\x04q\xe1\x15v\x8b\xf1\xf1qt:\x9dX\xf8\xc2d}0\x18\xf0\xd3O?\r\x00\x98\x99\xd9x<\xcb\x14\x88D\x04\x00\xfa\xfd\x01B\xca\u0485^\u03356\x98\x9e\x9e\u008e\x1d\u06e1\x94\xac\x158J'\x90\xe1\xfaU\xbe\xb3\r\xdc\xe0\xe0\u06e1\b$\x05\xa8! &%DGB\x06\xebT$\xd71%\xfe\xd9k\x18N\xd5\xca\xf6\xdaNX\xfe\xd4\xea\u014a\xc6%\xe4\xc6\f\xaa#\x915\xc8w\xe9\xc2y\xba4\x84S\xfa\f\x19N1\rei&6\x04\xaa)\\1\x1f\x97\xa0)\xd7\x01\xaf*\xb2k\u060c\xa7\v\x16\x0fc\x12\xabj4\x0f\xc5\xd6\xd5\x1f\"\xb8\x97\x18\xbf\x1b\x10\u00a9AE\xa8\x04\x13\x12\u0618\x01c\x12\xdc p\x96\xfa\xbe\xfb\x00\xee\xa4\v\x0f\xef\xa7\x11~\xe71\xad 7fn\xae\xc0\x95\xe2U\xf8B\x1es\xb8\x13\u03da:=\xd1\xf90Y\xad\u0757q\x9duE3\xb4n8\x1a\xa0\x13I\x10-\x01\xf2\xce\u0281\xcdR\x83\xd4D\xb2c\x1b2\xf6\xb4VC\x8dM`\xf6\x92WB5Z\u043a\x88!8l\x8dc^\x81D\xb7\xd7\u0179s\xf3;\xef\xb8\xe3\xcf\xf6:\x94\xe0\xfe\xf5b\xfeB\xc7+_ym\xfc\xfe\xf6\xdb?\xf8\xf9W\xbc\xe2\x15\agg7\xa1\xd1hF\xae\xf6\x993g\xf0\u044f\xfe\x1a~\xf1\x17\xff!\xbe\xf8\xc5/\xe0\x89'\x1eE\x96)\x8c\x8dMDY\xfc\x85R\u052d\xb5\xe8t:\x18\x1bk'\u015c\u0207m\xa8^\xaf\xdf\x04\x80\xa3G\x8f\xdd5\x18\xe45\nc\xc5f\xb9\xf0\x8b\xb91\x1aY\xd6\u009e={0>1Q\xcd4\x88 \xa4\x84\x94\n\x82\x04Z\x9di\xa8\xac\xe9\xe9f\t\xea\xc1\\\xcd\xc0\x04\x1cF>\xae\\\\\x183\x94/:af\x99:\xf5\xb9\xf7\x9bF\x16?pR\xf2\u05fc\xfc\x86\x13t<&;\xe6\n\xba\xecH\x90r\x85/\x13\u0085\x10\xd7d\xff<\x94\xe7@\xb5\u007f\u02cc\x905\x04dS8\x8c\xbc)j\xf8\u007f\x8c\x81[\u02dbf\xad8\xb6\x11~\xf0\xc3I?\xe9e`\xbdK.\xfb\xc7S\x92\xa0(q,\x0f\x1ds[\xc0N+7x\u039c\x1f\x8ejJ\xc8\xcc\r\x87S9\xbf\x15\xf0A\xda\x02j\\A\xcdd\xa0\x86g\xe6\xa4^\x91\x01M\xe2\xe1\xd79\xa5\x1b\xb9\u0398u\tk48\xc0*)~\x9e|\x05?\x16d\x00)\x00\x1eR1\x9ek\x1e`5\x1b=n\xea0\v\xb3Ei4\f\x03\x1bw\xecE\xa3=\u9b54)\x16sv\x9fcQ\x14\xa5VJM\x1d;v\xe2\x06\x00\xe8v\xbb\xeb\xc5\xfcG9\xee\xbc\xf3\xab\xa1H\x1c\xbd\xe9\xa6\x1b?q\xc5\x15W\x94\xe3\xe3\x13h\xb7\u06f5b\xf7\xc5/\xfe\t\xde\xff\xfe\x0f\xe0\xfd\xef\xff ~\xe37\xfe\t\xbe\xf4\xa5?\xc5\xf2\xf22Z\xad\x0eZ\xad\x8e\x17\x1e\x89\x1f\x9b\x822\xf8\xb0w:\xe3\x18\x1b\x1bK\xb0rAy\x9ec\xf3\xe6\xd9\xcbv\xee\xdcq\r\x00l\xd92WfY\x16\xa5\u0100\xa3b^\x88\x03P\"7h\x12B@J\t\xa5\x14\xb2\xccm\xabw\xed\u0685,s\x03j\xe1y\xbbB*\x88,\x83l4\xd1\xecL\x82\xbc\x02Tp\xe2Sb\x00.-\xacq\x96\xa6<&\xc1\xde\xc3#4\x86\x888(\xa2\xfc\x9c_\x88\xe4\xf1\x02\x1d9^\xa0\xbeS\xdb\xcb\xd0;N\\$\xa4\xc3\xf5\x95$\x84H\u04c0\xa1\xa7\x8f\x19\xfe-\x95\xa3\xfb\x89\x967\xbdZ\xcd@\xad\t\x84^\xe4;1\xa2\x93\xe7\xbammH\u0671\xce^W\x80\xbc\x8d\xc1h\xbe|@3\xa8%\x9c\xeb\u2e02jJdaA\x92\xc1m\xd1\xedDB\u0407h\t\xa8i\x05j\xc9\x1a\x1e\x9e\x12i\bnA\xaco\xd9\ua2f0\xd1\x06\xa6(\\Wn\x8d/\xa8\xa6\xf2_I\xbe\b\x0e#bb@q\xa2/p\x98y\xa4*\xc2\xc2x\x13.\xf6!7\x96\x03\xben\xc1l06\xb1\t\xd3s\xbb@B\x80\u0640\xad_\x04\xb8Zu\u03df?\x8f'\x9ex\x82\x00\xe0\xe0\xc1C\xeb\x98\xf9\x8fr\xec\u06f7\x0f\x9f\xfd\xec\u007f\"\x00\xf8\u065f}\xff\xff\xb8\xed\xb67\xff\xdb\u05fe\xf6\x06\x8c\x8d\x8d\xa1\xd9lal\xac\r\xa5T\xbc\xfdC\x0f=\x84?\xf8\x83?\u0107?\xfc\x9c\xd2\xd3\xda\x00\x00 \x00IDAT\xf7\xf0\xa1\x0f}\x18\x9f\xfc\xe4\x1f\xe0\u0211g\xa1T\x13Y\xd6B\xa3\xd1F\xb3\xd9F\xb3\xe9\xba\xfb\xe0\x1f\xfe?\xa7\xc83\xa4\x14\xd1\xf90\xc8\xf6\x8d1\xba\xd9l\xa2,\xcd\x0e\x00\xb8\xf6\xdak\xdb\xe3\xe3\x1d\xdf\xd1V\x17\xf6\u0673g]Q\xf81\xd11]\xe1\x16PJ\xa1\xd9l\xa2\xd9l\xa3\xd1h#\xcb\xda\u0232\x16\x94jB\xca\f\xd62~\xf8\xd0\x0f\xf0G\xff\xe9\xb38\xe3\xe7\x00 \x82\x94\n$\\zK\xa3\xd3A{n\x1b\xa8\xd5\xf2\x83\xac\n\x12\x11p\x81\u077a\xb0\xc8\x19\x184\x05\n\xf6A\f\xd1\x1a\xb7^\x1c\xd2\xee\x94\xd2\xeb<\xc1Vb\xafK/\xae\xa6\a\x9a!Y\xb8l\u034d\nb\xdc\xcb\xe0}Z\x8eLW\x19\u0090\x01\x95\xc7\xc9[\x12\xa2)@-\t\x1a\x97\x80\xac\x9a\xd1:\uf15egQ\xa1\x17t\xb2\x19B\xb5\x93\xe2\xcc0\xbe\x0eJr\xc3Lz\x9e\u05c2*<\xc6\x19\x92M+g5\x9c9\x93.\x12\x95\n4\u0707\x90@c\\B\x8cK\x17\xe3\x96t\xe5<4\xba\x88 \"\x8fp\x8ed\x86-K\x18]\xbaa\xa7\xa9\xe0\x15\xd808\xf5 \u007f\xf2of\xeb\x87\xe7\x0e\xce\t[>\x1b\x86\xa1\\\xd9\xf4R\x98eQ\xb2\xb33\x1a\xaa\xd9\xc6\xc6\xdd\xfb\x00!\xa0K\rcu\x14+1;\x87\u04d5\x95\x15<\xf7\xdca\xed\n\xfb\xe2\x05Y\xcc\u0545vB\xbbw\xef\x01\x00~\xcdk\xae\xa5\xfd\xfb\x1f\xe4\u007f\xf4\x8f~\xfd\xb7\xdb\xed\xf65\x00\xdes\xef\xbd\xf7\xa1\xdb]\x81\x94\x92\xad\xb5d\x8c\x89)\u073d^\x1fw\xde\xf95\xdcu\xd7\xd7\xf1\xd9\xcf\xfe1\xae\xbd\xf6Z\xdc|\xf3M\xb8\xfe\xfa\xeb\xb0s\xe7E\xe8t\xc6 D\xe6\x8bc\xfa\x81\u05c9\xda\xf2\xef\xbc\x1c&\xd3\xfb\x8a\xb2'\x84\x10EQ\xa0\xd7\xebZ\x00\u0631c\xfb\x9f\x13\xd1B\x96e\x1bz\xbd\xea\x12x\xf2\u0267P\x96\x03dY\xf6\xa2\xf3N]\xe7,\x9c\x87\xf8\xdf\u025am\xb0\xb4t\x1e\u01cf\x9f\xc0\x91#Gp\xfc\xf8\t\x9c<y\x12\x87\x0f\x1f\u0191#G\xf0\u88cf\xe1\xe0\xc1\x83\xf1y\x86\xe1 3C\x00h\x8dOaj\xcf\x1e`C\a\xf6\xe42\x94\x97\u07a7\u0233e@\x17\x8c\xa2\xeb\xba')\bMIh\n\x8a\xc5 \xad\xa1i?Z\xeb.\x87\xa0\x98\x17W\xc69v9!\x10\x813\xc7\xc9f\x00\xb4\xc8qg\xc2\xe4Ph\x81!;X\xe9\xb0uj\xf8Y\xc0\xb8\xc7\xc9m\x15FM\xab\x1e\x92\xd6\x00\u007f\u0590\xbf\xbf\xc0\xf9[\xb8\xbaG\xfe\xf3N\xbc\x06\xda\x1e\u051b\xecY q\x81d\x88&\x81\xa7\x14\xd8j\xf0\xb2\xae\xf8\xf0\x1c:AFF\x02\u0658\x9bs\x00\x80\xb1\xec\xe8\x82\xc3\n\u0580\x97\x87\x957\xe5\xa32`u\tS\x16\x11+\x8f\xa2!O)D`\xb2\xf8\x02\x1d\xbd\u030d\x05$\x83U5r\xae\xa2\xf5\x1c\xb6DI\xf3Vy\x88\x91_|4\xa81\x86\u03b6]h\x8cM\xa2\xb7p\x06\u031e\xb1B\x95\xe2\xb8(J,..\xb2\xbb\xb6.L\xc3$\x85\v\xf4\u063f\xffA~\xfd\xebo\x15D\xd4\x03\xf0S\xbf\xff\xfb\xbf\xf7\xb1F\xa3\xf1\xaf\x1f~\xf8\x91\xe6\xe9\u04e7\xa9\xd7\xeb!\xcb2ffbfdY\x06k-\xf2<\xc7\xfe\xfd\x0f`\xff\xfe\a\xf0\xb9\xcf}\x1e\xbbw_\x8c]\xbbvc\xf7\xee\xdd\u0633g\x0fv\xec\u060e\xad[\xb7`nn\vfg7a||<\xaa\xba\xcar\xf0w^\xd4\xd7\xc0\xf0\xd9\xc3\x14\x12\x00\xf6\xec\xb9\xe2\xc9-[\xb6\u0627\x9f>\x00cl\xfc\xa4\xff\xf0\x87?\xc4\xe2\xe2\"6m\x9a\x83R\xcaE\\\xbd\xd8\x12l\n\x94e\x89\xb2,\xdd\x16\xd3\x1b\xf5\xaf.\xfeT\xf3\x86)\xcb\x12\xcb\xcb+8y\xf2$\x9e~\xfai<\xfc\xf0#8x\xf0\x10N\x9e<\x89\xf9\xf9\xb38wn\x01\x8b\x8b\x8b\xab\xa0 !\x84\x8bw\x13TQ-\x85\xc0\xc4\xec\x16l\u06b5\v\xa2\u04c0!\x86\xb4\x94^\xe3\xae\fy\x86\a\x9d\xd70Z`0&\xd1\x17\x04E@K\xba`\t\xe1;+Z+K\"tb\xb6\x1a8\xf3*\xb1\xca\xf3\x15B\x1ajS]e\xe4\xcc\a>\x18G\x97C\x11\f\xa1\xbc[\x9f\u007f|+\xe0\x84Ha\xa0;&\u0717/\xb0\xc4\u0568r5.\x9e&\x85R\xa2~J\xb924\xf2\x8c\xe3z\xc0\xe4!\x06\x1f\xecL\xf0\xd1s<\xa2\x9b\xf7\xc3\xc1\x04\xbe\xaa\xa0\x1f\xbf\xc06%\u0318\x85Y\x04\xac\xa9\xcf\x17\x049\aFa\xbc{\xa1?5c\x1dN.\xd3A\xb1\x1fZ\x83\xc3{R\xedI\xac\xd10y\x0eS\x140\xba\x04[\xedZ\xfc\xe4\u06b1\xfe\x04+\xd3,[\xb9*\x02n\xb1\x94\x00\xca\xc4x+\xce\u0753E\x88\x03'\xde\xc2\xc2\xe1\xfd,,\x1a\x13\x1b1\xb1e\x17z\vagY\xf1\u0549\x88\x8a\"\u01d6-\x9bw\x00\xc0\x9f\xfe\xe9\xff\xd0G\x8e<K;w\xee\xe2\xf5b\xfe#\x1e\xdf\xfa\xd6=\xd6\xf3\x12\xf9\xd7\u007f\xfd7\u007f\xf7\x81\a\xee\xfb\x9b/\u007f\xf9+\xff\xfa\xde{\xef\xbb\xee\u0631c\xe2\xf8\xf1\x13T\x14y\xd1l63f&c\f\x84 \x94\xa5\xeb\xb6WVV\xf0\xf0\u00cf\xe2\xe1\x87\x1f\x05\x00t:\x1dLOOabb\x12\x1b6L\xe3\xe2\x8b/\xc6\xf5\xd7_\x87\x9bo\xbe\x19W^\xb9\x17\x13\x13\xd3\x00\x18y\u078f\x9dp(p/\x16\x96\xf1\nV\f\x06\x03\xf4z\xdd\xda}\x01\x84F\xa3\x11Y.\x00\xe4\xf6\xed\xdbOH)f8\x99\xe6\xddw\xdf\xfdx\xee\xb9\xe7\xb0i\xd3\x1c\xa4l`ii\x01\xddn\x17\x83A\x0e\xad\u02f8\x05t\xb8u\x16\xe1\xa3\xc1 \u01c9\x13'p\xe0\xc0\x01\x1c8\xf0\f\x8e\x1e=\x8a\x85\x85\x85\xa8*]\xdd\xe5{S$\xff\x1c\x8d1\x18\f\x06X^^\xc1\xd2\xd2\"\xce/.aqq\x11\xf6yv\aB\b\b\x95\x81H\xc0Z\xe3!\x1a\u9f22\x1bMl\xbb\xea5\x98\xd9~\t\xca~\x0e\x16\x04\xab\x00a\xaa\xe2\x14\xa5\xf8\f\xa8\u0082\x96\x19\xc2\x00yG`@\x84B3\xfa\x9a\xd0\xc9\x18-%\u0410\x14\x87\xa2qx\xe7\xb1\xe1\xc0N\vN\xb9\x15\xe6\x1e\xba\xb3Q\x85\x8dc\xbdI\xfd\n\xe3\u06d1\xaaE\x1d\r\xde\x15\xeb\u042d\x1a\xc0\x04\xb5a(\xd8M\x01LHG\xbf\xe4Qv\xb5\xb4z\x96IC\xbf\x1b\x81\x8e\xd3Z#\xd2d\x9e\x10\xa4\ubafb\xfb\xa1\xbf\x8c\xb5\xaf\xb2\x8cu\xfe(\x8c\x82\x19yia\xfa\x16\x82\x192&bq\xb5\xc6\b\x00}\x03,k`:s\x96\a\x89\x86I\x10\x0f\xa9Y\xa9\xfe8\xc6@\xe79L\x91\u00d6E\x84W\x90\xc8\xf9\u00d0\u0630\xf5E\xbe\x1ab\x82\xfd\xfb.\tF\x02\x9a-`\xac\xb7\xbd%\b\xff\xees|lO;\x14>\aV\t\b\xa3\x91\xb5'1\xb5\xfd2\x9c~r\xbf\xff<+\b\xa1\xc2g\x99\x8a\xa2\xc0\xec\xec\xecnf\xdeBD'\xf1\u0084\xd2\xf5b>\x8c\xd9\x02\xe0\xdf\xfd\xdd\u07e1\x8f}\xec\xe3\xfc\xeaW\xdf\xf0\x97\xcc\xfc\xe6\xff\xfe\xdf\xff\xeb\xffv\xcf=\xf7\xfc\xd2\xe1\xc3G\xdfx\xfc\xf8\xf1\u01a1C\x87\xc0l\xb5\x94JJ)I)\aKh]\x87P\xba\xdd.\xba\xdd.\x80\xe3\x00\x80\a\x1f\xfc\x01\xbe\xf1\x8d\xbf\xc0\xb6m[\xf1\xa67\xbd\to{\xdb[q\xcd5\xd7`\u06f6- \x1a\x8b\xf0B(\x80U\xd7N\x95q\u03e8\x8e\x87\x19\x8dF\x03R6\xb0\u007f\xffw\xf1\xc8#\x8f\u059eO\xa3\u0450y\x9ek!\xe8\xa0\xffy\xf1\xab\xbf\xfa+w\x18c\xaf\x96R\xc6b;??\x8f/}\xe9\u03d0\xe7\x05\x1ez\xe8!\xfc\xe0\a?\xc0\xc1\x83\x87p\xee\xdc9\xb8\xb0k\xa0\xd9la|\xbc\x83\x89\x89\t\xb4Zm0[\xcc\xcf\xcf\xe3\u0109\x93\xe8vW\x90\xe79\xca2\x9c\xbf\x19\xda)P\xf4\x1c\x0f\u07ff\x18\x8a'\t\x01!\xa4S\xcdI\x05!\x1c~EF;xE)\x90\x14\x98\xb9x\x0f\xf6\xdc\xfcV\xa8\xc64\xfa'\xcfC\x90\x80\x95\x1e\u03b0U:\x1bK'\x19g\x02d\u0260\x15\x03bF1.a\x05\xa10\x8c\xd202a\xd1\xca\x04Z\x8a\u0410\"F@\xba\xady(h\xec|\xae\x87\xdc7WA\x15\x9e\xed`9\x91\xfb\xd7\xec\u0513n\xd8\xd3\xe10\xa9\\\xa1X6\xe0\x81\x01t\x05MXCN\x8cSX\xb0P0\xd2CM\xab\xb2,|a\f\x16\x05\xb5\x1a\xef\u07cb\b\xf7\xa4[\xfe\xba\\?-\xd6a\x14\x11\xb1\xf1\xe1\xa0\xe9\xb0\xc8P\xeaT\xb8\x8aM\tm\x19}m\xd1-\x19\xb6k\xd0X1N\xdf\x14\x16\xaf`\xe3\fr\xe7h\x18\xb4l\xdc\xef'$\x023\xd1V(\x8e\xa7L\xa6\xe9\xae\x04k\xdc\xc0\xd3\x149LY:Z\xa0\xc7\u016d\xad[\xe0Zk1l\x04C\x9c:(8\x89\xbf{\xc1\xb8\xc2\xe6\xfd\xfb\x1b\x94\xa3a\x12\xab%\xa0\xa5\xff\xbb\xa2\x84\x18\xeb`|\xd36d\xcd6\xcaA\x1fBH\x90l\x00\xa6\x04\xc3\xc0\x18\v\xa5\xb26\x80\v6\x13\xf4\x82.\xe6\xe1\xf8\xd8\xc7>\u039f\xf9\u0327\xe9\x97~\xe9#LD]\x00\x9f\a\xf0\xf9;\xee\xf8\xca\a\xbe\xfd\xed{\xfe\u0791#G\xde\xf6\xec\xb3\u03e9g\x9f}\x16\x83\xc1\x00D\x8c,SPJ\xc2\x18\x05c\u071bQ/\xc8\xce3<\xcfs\x9c?\xbf\x80\x83\a\x0f\xe1+_\xf9*^\xf3\x9aW\xe3u\xaf\xbb\x15\x97_~9\xe6\xe6\xe60;;\x8b\r\x1b\xa6166\xf1\xa2\xceY\xeb\x1c_\xff\xfa\xd7\xf0\u06ff\xfdoq\xe0\xc03\xd1\x1e\x16\x80m6\x9b\xb2,\xcb\x03\x9b6\xcd>\x19n\xff\x8aW\xbc\xe2\xf4]w}}U!\xfd\u0527>\x8d?\xfc\xc3Obqq\xf1y\x16=8\xfa\x9f\u007f\x8c\x88iz\xe8$\xfc?\fR]\xb3\x13\xf2U\xd9\xc3.\xbc\x9a\xa7;bq%\xe1L\xfe\x11\xd9B\"^_\u0194\xf1\x82\x93J\xa23\xb5\x01\x9b\xf7\\\x8d\xab\xdf\xf5al\xdc{#V\xe6{\xe0\u0702\xd8\xfb\x1bJ\xc0\x12\x83\x02\f`\xad\xc3.|\xbc\xa7`\x8b\u018a\x1bD\xe6\xe3\xd2\xd9\xd22PX\xa0\xc8-\xba%\u0412\x16c\x99@S\x0e\x85\v3\xc5b\u00a3\xe4\xf7\xb4\x1ax\xb1pp\x81\xa5\x91\xe4\x16\xf7\xba\x19\xef\t3.\xdd\xcfK\xeb\x8a9\x85N\u0531G4\xbb\x8eU-\v\xf0\x84t\v\x95M\x89\x1c<Zk\xef!\b\xe3\u03ff\x1a\xfer\xb4\xc7\x1dAV\x84\x89\xefy\x1d!\xaaM\x15(\x88r\xe0\xc56T\xf3\u0231\f\xe4\xa5EO[\f,\x83KF\xabo\xa0\xb4\xdf}\b\xb8\xc5\xcc\xc3(L\x0e\xd6p;N\v\xb1\xa8\xbd\xe0K\xd6c,\x98\xeb\x93W\"\a\xad\fr\xe82\x87.\x8a\x04/\xd7Ug\xee\xa5\xfcQ4\xc4\x0e\"\xb4!\t\xc9\xc7\u00b1\x0f\xe1h\xf8HA\xee\x01\xa6tx9{\\\x85\x02F.\\\xeek)\xdd\xeb+\x8c\xeb\xd2\xd9X4:\x1b\u041a\u0704\xa2\xff\x1c\x84\u02a0l\x03F\xf7aJf)%\xba\xdd\xee<\x80\xf3\xe9\xc7h\xbd\x98\xff-\x8e\xb7\xbf\xfd\x1d\xfc\xe9O\u007f\x12\x1f\xf9\xc8G\xe3\xcf\xde\xfd\xee\xf7|\x1e\xc0\xe7\xbf\xf9\xcdo|\xf0/\xfe\xe2\x9b7\x9f8q\xe2\x8d\xe7\u03df\xbf\xf2\xc0\x81g0??\x8f\xa2( \xa5\x8d9\xa2\xcc6v\xec\xc6T\u0662\xc6Xt\xbb]\xf4z=\x1c9r\x04w\xde\xf95LOOc\u01ce\xed\u0631c'\xb6n\u0742\xed\u06f7ann\x0e\x9b7o\xc6\xc4\xc48\x84\x90qq`f\x14E\x8e\xc1\xc0-\x0e\xa7N\x9d\xc2C\x0f=\x8c\xbb\xee\xfa:N\x9et;2\xa5TP\xac\xb2\xb7\x8c]y\xc3\x1b\u07b4\x10\x9eK\xa3\x91}gnn\x0e'N\x9cDQ\x14\xb1\xa8.,,<\u07ee\xc5\xc1\x1b\x9e&XAB\xd5\xef+\xc3)\x8f\x8bZ[\xfbZ\xabx\a\x8f\v\x12\x94\xd8\x0f\xb3\xef\u0185\xc3\x14\x85\x80j4\xd1\x1c\x1bGgf\x16\x13\xb3\xdb\xd0\xd90\x8b\xe6\xf8$Tg\x12\xed\xd9\x1d\u0630\xfb\x1aL\\\xb4\x0fK=@,\xf6\x91\x19\xac\x9a\x00\xd6\xc9\r\xec\xf2%\xfdV\x98\f\x90u\r`\x81bR\xc24\x84\xebf\xddn\x1a]\xcb(\xacu\x98\xba\xb7\x96M\x85!\xa1C\x14\xabL\xb9\xea]+E\u067a+\xa4\\\xf3#\xa9\xa2\xc8\x02D\xcb\xc2]=\xc1\xc6\xc3Z\x8e\x1eV,\x1dvn\xfa\x16\xda\x14\x10ZAN(\u020c|\xe3H\xf1\xf1\xac\xc7E\xc2p\x17\xfe\xb1-\x1c\x13\x05a\xf8\xeb_2\x9bb\xdbT\x15a\xf6\x03\u3c01\xa0!*|x\xba6\xb2\x88\xc2\xca\xe0\"\xd2\x06\x861(\x19\xb9\xb1n!\" \xcb\x19\xb2`P(\x98\x02\xb0YP\n\xf9\x9d\x88f\xcf<\x12\xc8\x18PK\xae\xa0sS\xa4\xebfmg\xc2ZC\xe7\x03\xe8|\xe0\x18,\xa13\u05fe\x90'\x9f\u05fa\xd9V\xf5\uf2ad\xe3v7\xc2\u007f^\xdc{\" \xc8\u0090\u007f\rQ\t\xbb\xb4\x00J\xc1q\xe1v\x10\x8e\x00\x1b\v\u065a@kj\x13\x96N\x1c\x82 \t)3\xf7;\x06g\x8d\x06\x0e\x1e:\xf4\x04\x11-\x02\x10\x8dF\u04ee\x17\xf3\xbf\xe5\xb1c\xc7E\xab~\xf6\xf5\xaf\u007f\x8d\xde\xf6\xb6w\xf0\x9b\xdf\xfc\x93\x9f\x03\xf0\xb9S\xa7\x8e]\xf9'\u007f\xf2\xa5W\xed\xdd{\xc5\rg\xcf\xce_\xdb\xeb\xf5\xae\x9f\x9f\x9fo\x9e:u\n\xddn\xcf\x0f\x01\x1d\xf4R\x14\x05\xfa\xfd\x01\xf2|\x00c\x92\x0f\x871\xe8\xf7\xfb\xe8\xf7\xfb8q\xe2\x04\xbe\xff\xfd\xfd\xa8\nn\x03SSS\x9e\x19#\xfc\x82`\xc0\xec\x06\x86E\x91#\xcf\v\xf4z\xbd\xf87\x81\x8b\xad\x94\fjI\xbe\xe4\x92K\xb0o\u07d5O\x11\xd1\xe1p\xbbV\xab\xfd\xec\x8e\x1d;\x8e>\xf9\xe4S;F\xe3\xda\xf5\">\xfc}\x1a1\x97B)\xe1{_\xb8\xf9\x85HzD\x04\xa9T2\xfd'\x80\x84\x13\xffd\x19\x1a\xad1\x8co\u068a\x99]\x97c\u64bd\x98\u06be\a\x9d\x99Y\xb4'\xa7\u045a\xd8\x0096\x01-[0B\xc1\x88\x06\x8a\xdcb\xb9?@\xd6+\xd1.Qc2\x90\xf5A\x04\xc1\xf1\xce\xc0o\xe3\xb9R\x1b\u00b5\xca\xd9\xc0\x80\xc0\xc8'\x15LC\x80\xa9\u2a57\x86Q\x96\x8c\xbee\x8cenP\x9aIJ \x16\xdfuS:(\xad<\xae\xeb\x905\xd5\xcc\x0fC\x17\xccL\xae`\xfb\xdd>k\x06V\f\x88\xbdp\u01ba\xc1\x9f\xf10\x06\xc1y\x8c\xe8\x9c\xc1\xe7J\xa0\xb0\xa0I\x05\xd5\x10\x88\"R\xaeZ\u22bf\xed\x9e/\x0f\x85\x98\x86\x1al\x93?c[)+\x89\u073f\x1dN\\\x87t\x1c\xb3eH?J\xee9\x15\x86\xd1--\xfa\xda\xc2\xf8\xa1)\t@\u632co\xe2L \x0e\xab\xc3\xceE\x00d\u072e\x89\x19\xd0\xd6u\u045a\x19\x12\x80\x9c\xc9 2\x01k\xbdM\x82p\x85\u0616\x1a\xba\xc8aB!\x8ftD\xed8\xde\xc1{\x85\x19ll\f\xa7\b\u0316`o\xcb\x1e\x9b\x93\x82<\x1f\xdci\x15\xb8t\xb7\xa3j\xb5\x84\xa7\xa0\xc0\x92t\x1d\xb9\xa8\x16n\xf2\u05c71\x06\xb2\xd9B\u0599\x8a\xf7\x97\xc2TB\b\xf4\a\xc5\x00\x00^s\xfd\r47\xb7e\xbd3\xff\xbb:\xee\xb8\xe3+x\xdb\xdb\xde\xc1\x0f=\xf47\xf4\xe0\x83\x0f\xca_\xf8\x85_4ss\xdb\x1f\a\xf08\x80\xcf3\xf3\xb6\xff\xf6\xdf\xfe\xcb\xdc\xe1\u00c7ggf6\xbe\xe9\u0739\xf3;\x1ey\xe4\xe1%)\xd5E\x1b6lx\xeb\xc1\x83\a\xd5\u0463G\xb1\xbc\xbc\x82\x95\x95\x15,//#\xcfs\x0e\n\xcd\xe1\x8e\xd5u\xdf\x05\u039c9\x833g^\x98\x16\x18:q)E\x847\xac\xb5\xbcy\xf3\\v\xf9\xe5{\x06\xb7\xddv\xdb\u007f\x01\x80\xc7\x1e{\x98\xf6\xed\xbb\x86?\xf8\xc1\x9f_z\xf2\u0267\xfe\xea\u0211\xa3\x1fx\u4447\xd1\xeb\xf5G\x16\xde\xf4\xbc\xc2\xf7C\x85\u007f\xe4\xdf\x11\x11Z\xad\x16\t!\xe2\x1c!\xfd\xbb\xb4\xf8K\x95\xa13\xbd\x01B5AY\x86\xf6\xe4\x06\xcc\\t\x19\xb6\\~\r6\xee\u078b\xf6\u62d0M\u03601>\r\xb4\xc6\xdd6\u057a\xf3\xe8\x97\x1a\xa56\xb0\u0682M\x0f\xb64\x10\xc6uy\xc2$\x92\xf9\x9a\xf2\x93]\xa7g\x03\x8c\xe0)e^\x90\x130c\u0577\x00k\x14\x13\n\xa6\xe9\v\x9cE\xbc\xb8\xf3\u04a2,\x81AF\x18oJ\xb4\x1aU\xa7\xce5x\x82V\xe1\xc4\xe9\x8bfb\x91\xe4\x88)k\xc3\u0425+.\xc62d\xcfB,\x1b\x90u\x85-\x868\b\xef\u07c2D\xe0h\x00\xbbl\u07023\xa9@\r\x82\x02\x90\x81\x90\x81#\a\x9c\xfd\xf3\x14*a\xa2\xa0\xdaYp\xbaCH\xb6\xfa\x82\xea\u04c0\u06ae$\xb0K\xb8>-\xd0\x06\xe8Y\x87\x8b\x97&Q\u0252\xdf\r\xf5-T\xe1vI,\xfc\xfba\x00\xd2\xd6\xe3\xfc~\x87\xe1'\xa8\x82\x00\xd6@I\x8cr\xd9@\nB\xb61\x83\xf23\x83R\x1bp\xe9\xf0q.\x1dk\xc5j\r\xa35\xd8w\xe4)\xac\xc2\u0182\x8d\x89\x83P\xa7\b\xe5\xa4kO\xb6\x1b\xecp&1\xb0\xb0%;s-\xef\xfbB\x11\xa7r,\x1f\xaey\xe1x(\xce/ \xa42H\xcflcx_u6\x00;\xe7\x97M\xb3\x9b'\x01`\xff\xfd\xf7\x99\x93g\x17\xd6\a\xa0\u007fW\u01fb\xdf\xfd\x1e\x8f5\xbf\x8a\x01\xe8\x13'\x8e\u04b7\xbf}\x8f\xba\u77bf\x12\x9f\xfc\xe4\xa7\n\":\x1e&\x9d\xcc\xfc\r\x00-\x00\xdaZ\u04fe\xf7\xde{7\x1e8p\xa0\xdd\xedv_\u007f\xe4\u0211\xf7?\xf6\xd8c?q\xf8\xf0\xe1\xa9n\xb7K\v\v\xe71\x188_\x14\xb7]\x17\x15\xc5\xcdo\xfd\xa8>?\x1b\x8aJ\xab`\x8e\xa4Sf\"A[\xb7n\xa3\x9bo\xbe\t\xb7\xdcr\u02ff\u0677\xef\xea\xbb\x01`\u07fekB\x90s~\xd7]_\xfb\xbd,\xcb\xde=>>>\xfe\xc0\x03\xfbiqq\xe9Gz-\xaa\xee_\xa1\xd9l\x12\x11\xa1\xdf\xef#\xcb\x14\x8f\x8d\x8dQ\xbb=\x06\",l\u07fe\xddn\u06f6m\xfe\xfc\xf9\xf3_\x1b\f\xfao\xfc\xde\xf7\xee{e\xea\xff\x12\x1c\xe1\xc6&7\xe2\u019f\xfbUl\xdc\xfbJ\xa09\x86\xf1\x99-hM\xcd@\xb6;0\xaa\x05C\x02eYb\xa5,\xa1\xcf/:\xb8*\xf0\x95\xa9JI'\x02\x98\x04Di\xdc@3TTI\xd5\xf7\x9c\x14\xf6\x00lp\xc5\xf1vX\xad\xc3iI\x00j`AF\xa3\x98\x94\xd0-\xe1=^b\x1d\x85\xb5\x8cA\xc1(5\xa3m\x04&\xda@K\xc8\xc4\xe3|m<\xbc\xb4\fm\x19\x03mQ\x9a\nk\xb6\xceb\x1b:wb\x16\xa9\x19\x8d\x15\vU\xb2\x87\xa1\x82\xa0\x06\x95\xe8\xa66\xcfd\bKP\x03\xbf\x18\x8dK\x14\x19\x01\xdaB\x18\x8bL\xb8\x9d\x84\x92\x04\xa5\b\x92\x9c\u06b2\xdaI\x84\xe2\x9d\xf8\x8cp`\xab\x84q\"\xc7\xcf)\xaa\x97..H\xe9B\x95kF\xcfX\xe4NG\x19\xcd\u0442Y\x95\xea[\u0201\x89wB\f@3\xa4\xf1,\x16\x03\b\xe6\x88\xd4 @\x16\x9eQd4C\x9f/Q\x12\xa31)\xa1\xd8\xc0\x16\x8e\xb1\xc2Z\x03F;\uee9fc\xc1\x98\xaa\x90\a>\xb9\x87A\xd9$\x1cs\xb6\x91c\x8e\x80\x99\xb3\x05\x97\f;0@a\"}2:4\"\xb11`@X\x86%\xaa\xbbf\x06\xdez\x80\x1d\xbdk\xa21\xda)G\x01\xc7\xcc\xf2\xa1\xcf\xd5\u02b7\u0799\xff\xffrl\u077a\x83\x01\xd4\xd2\x1c\x9e~\xfa\t\\v\xd9^\x90\xe3\xa2\x05\v\xc2\x12@\xa8\x92\x8f\x03\xf8\xf4\xf1\xe3G\xf6\xfd\xf1\x1f\xff\xe7\x9f9|\xf8\xf0\xcf=\xfd\xf4\x81\xd9~\xbf\xbf\x19\x80XZZ\u00993g\x90\xe79\x8a\xa2,=.-\x84\x10\x94b\xd1\u00d0\a\xbb\xc3Z\u02d4e\x99\u06b0a\x9a\xb6n\u074ak\xae\xb9\xfa\xec{\xdf\xfb\xde\xdf{\xeb[\xdf\xfe\u007f\x00\xc0\x1f\xfc\xc1\xef\u04ef\xfd\u06af\xf3\x17\xbe\xf09\xba\xfd\xf6\x0f\xf2\xdb\xdf\xfe\x8e\xfb\xfa\xfd\xe57\\t\xd1\xce\xffs\u01ce\xed\xb7<\xf1\u0113\x8dS\xa7Na0\x18@)\x85\xb2,K\xa5\xd4\xf2\xe4\xe4\xa4PJ\"\xcb\x1a\x12@\xbe\xb0p\xee(3z[\xb6\u0309]\xbbv5\xa6\xa6\xa6N?\xf5\xd4S\xff\u03d5W\xee\xed\xddt\xd3M\xadK/\xbd\xd4LMM|\xf3\xaa\xab^\xf9\x8c?\xbf\xe6\xdf\xff\xfb\x1f\xfe\xcf\xdf\xf9\xcew_\x19,k\x03\x9c\x026 \xa9\xb0s\u07ed\xd8y\u02ed\xe8\x19\x86\x06A\x1bF^\x14\x18\xf4s\xb05\xb1\xd0H)!\xa5\x88\x1d\xec\xaay\x1e[d\x85u\x83&O\x13$\x9bx\v&xFJ\x19\x8c\x9e\xdb)\x89#p\xa7K\x8b\xe6\x12\x83\x8cB\xd9\x11\xb1[Mi\x88\xdaX\xac\f\x18%\bc\xd6A/JT\x9d\xab\xf5x\xb5\xb5\f\xc3@a\x18\xb9\x1f\x06\u06e4\xcb\r\x1d\xb01\xae\x90\x90\x01T\x9f]!\x0f<f\x06\x84\xae\xd8\x16\xc2T\u0404\x15\x04\xab\x1ch,\r@\x03G\xf3\xcb;\x12\x85 h\x03\x94\xdaV\x83l%\x905\x81\x86r\xe7\x9by\xb5i\xe0K\x8b\xb0\xb3!D\x8a \xa3>#\xa9\xcdU\xfdBg\xd8-p\xfd\u00a2[X\x18\x01\x88L\u01a4\x9ep{\x953\x1a=S=\a\x06\x84f\x90\xf6\xbb(\xeb0\xf4\xf0^\xa4\xf3\x06\xb7\xbb\xb2\xb1\xf8\x17g\v\xf4\a\x80j\x1ad\xa6pE\xdcxz\xa1\xadb\xe1\xc2\xc03\xf8\x98#t\xe2\xa6\xe2\x92G\v[\xe3\xfd\xcd\x03,ZZ\x98\xae\x86\xcd\xdd\xcf\xe3{b\xc9\u0767_\x04c\xb0\x89\x00l\x18\xe4\u05f6#\xc2\x0f_\r\xa4Tn\xbeV\x16a\x17\xc0\xadV\v\xcdv;\x0f\x9bo\xb7\x8f\\/\xe6\xff\u04ce\xcb.\xdb;\xf2\xe7\xcf<\xf34.\xbd\xf4\xb2\xf8\xefm\xdbv>\x06\xe01f\xfe\x97\xf7\xde\xfb\xdd\xeb\x1ex`\xffM\v\v\xe7_\xf5\xec\xb3\xcfN//\xaf\xec[^^\xdeND\x9ds\xe7\xce\xe1\u0739\x05\xf4z\xbd\x04/\xaf\x8a\xb9\x10\x8e1\xd2\xe9\x8ccjjRNMM\xa1\xd9l\xf6ggg\x0f]u\xd5U\xdf\xfc\xf0\x87\u007f\xfe\xd3ss[\x1f\x03\x80\xf7\xbd\xef\xe7h\u04e6\x19\x06\x80\xdbo\xff \u007f\xedkw\xd2;\xde\xf1Nn\xb7'\x1e`\xe6\u06ee\xbb\xee5\xb7\xdfu\xd7\xd7\xdf\xf5\xdd\xef~\xaf\xd9\xedvinn\xae\xdbh4\xbf{\xf3\u036f=\xf2\xaew\xbd#k\xb7\u06f0\x96\x1b\x9dN\xe7<\x91\xba\x87\x88\x06\xcf=\xf7\x1c\xee\xbb\xef\xfe\xf8\xbc\xbe\xf5\xado\xe3S\x9f\xfa\xbfG\x12\x8c\x17\x17\x97D:+H\x9b\r]j\fV\xfa(\x16\x80>\xaf@+G\xb7\xd3\\\x9f]\x06\x88\xe4\xf9\xecE\x84f\u021c\xbd\x89Q`\xa9$\x86F\x89\xc9\x16\xfb*\xcabd\xc4g\xac\xea\xc2\x12\xa8`4\xac\x06A\xa2\x1c\x93\x18\x8e\xbe\x16\x9e\x9fg\xc0X),\x06\x06hJ\x82\x0f\f\x82\xb1\x0ek7\x89\xe4=\xb0A\x86\x1e\xce\r\xf0\xfc\u03f2\x82\x91\xe5\xbe`\t\xf7\x1aH\xcd\x0e~\b,\x99\x94\xefg<\u03ac\xdc\x0e\x83\fC\r<\u04e2I(%\u0570m\xb0\x13\xe7\x14\x96=\xd4\xe2DS\xd2\xf3\u01a5p\xcf\xc1Y\n\xa0bQ'\x9c\xf2\xb8T\xfa\x81mQZ\xf4s\x8b^\xcen\xe1 @HQ\xdfa\x12 \r\x90\xf5\rD\xe9\x19 \xcc\x0e\x177\u026e*\x95\xd8&\x19\x9b\x1c\x02 `\x00m\xc1\xc2\x02\xb9\x81\x19\x18\xf4\xdb\x06J\x19\xb4\xc8\xe1\xe9\b\xbe*1\x1e\x8e\xa3\xdd-\x12qP\xc57\xf7\x81\x15\x89X\xc8\x18FQX\xe8\xae\x06\xe5\xa6fU\x1f\x98J\x14w\x1d\xceg\x80-\x03\x86\xc1B\xfa\x1cU\xf6\r\x84\xdbY9\xb7\xc6\x02\xc2\u04c3\x1d;\xcb\xdd\xf1X\xa7\x83\xe9\r\x1b\a\x00\xf0\v\xbf\xfc\xab\xb4u\xd3\xf4zg~!\x1c\xa1\x90?\xf6\xd8\xc3x\xcb[\u0789\xa3G\x0f\xa7\xdd\xf5\xf7\xfdW(6\xaf\xf8\xeaW\xbf\xbc\xf7\xe8\xd1c\xaf}\xf0\xc1\x1f\f:\x9d\xb1\xab\x1a\x8d\u019e\xc5\u0165\xb2(\n\x18\xe3\x042Y\x96\xa1\xd5j\u0271\xb16///?\\\x96\xfa\xe8\x15W\\Q\xec\u06f7\xf7\xe1w\xbd\xeb=\xf7\x12\xd1s\x1f\xff\xf8?\x03\x00\xbc\xf7\xbd\xef\xc1\x87?\xfc\xf3\x1c\xa0\"\x00x\xc7;\xde\xc9\u007f\xf5W\xff/n\xbd\xf5\x8da'\xf1y\xffU;>\xf3\x99\xcf\xfc\xad\x9f\xf7\xa1C\a\x82]BK)IB\xb8\xc1\xdd0\xe8`u\x89\xbc\u06c7^\xd10\xd0>\xb6\xcdC\x1d\x9c$\xb1F\xaa\x9cg\xba\xa0^\x1c\xc8\x02\xaa\xe0\xd8\xe5\x11\xfb\xa1\x19\b$\xb8\x1e\xda3\xd4\xd2\x13\xb9A\x1bQ=U,\x82\xd1\x02\x10\x1ah,\x1b\xc0\x00&\xab\x84\x8eQ\x96\xe2\x17X\x80}\a\u0315\xfd\xe9pr\x19\xd5a\xb3d\"\x8a #R\x1a\xc8r\x1b!\x05b@\x1a\xf7\xfc\xaa>\xad\xae\xd4$\xf8\xae\xd6\x12\xac\xf4E\xdd\x12Tn\xd1,\x9dK\xa1n\xb8\xd2#\xe06G\x94\x88?K\u02c8\x1a[r\x9d\xb9c\xbe\xb8b.C\x81\x17\fEa1r\x85\xbcdF^X\xe4\x03\x8b\xbc0\xfe\xf5&\a\xfb\xd0\xea\x18S\x99[\x88\xbc\xfa@\x90\xf5\x1c\xf2Z\xa6\a\xd5\xf0*f\v\xd6\xd6\v\xe8\r\xac\x1fb\x82\f\x98\f\x044\xa8\xb0\u8d41\\0Z\x02\xf0\x96\xe9\xc9{jcQG*\x18\x8a\x85\xdc\xcd*\xc2\xe3j\x03\xf4\xfb\x1a\xbao \n\v\x19\x86\x99\xfe=g\"\x18\xb8\xc5UD\x18T@\xa02\x1b\v\x10Tx=!\x04t>@\xd1[\xf2\xbb\x00\xe3rA\xfdy\x8e\x8fOb\xe7E\x17K\x87\x02l\xbb \xeb\xda\u02f2\x98\x87c\u07fek\xe2\xf7\xa7N\x9d\xc0\xdc\xdc\xd6Q\uc387\x00<\x04\xe0\x8b\xbe\xb8w\x00\\\xe2\xe1\x9a\xe1\xfeQ\xfa\x06\xebq\x1a%3\x04\xf0\u05b7\xbe\x15\x9f\xf8\xc4'\xf0\xda\xd7\u07bc\xeaw\xb7\xde\xfaF\x1c=\xfa\x1cv\xec\xb8\xf8E?\x97\x83\a\x9fF\xa3\xd1x\u07bf\xf5\x85\xdcw\xa0>\x1e\v\xa6>\x00\x00\x90\xf7\x96\xd1]8\xeb@\xabA\t\xce\b\xba)`\xd9\x0f$\x13\x18\x82\x13~\xb5\x1drV\x92\u05ba\xe2g\xfd\xb0\xcc\xfa\xa2\xe7)\x11\xf4|#\xdbT'\x13\a\x9d\x9c\xdc\xd6A\x17T02m\xc1\r\x81A[\xc0\u02aa\xa0\u04c8D\x1c\x1e\x01y\xa64>\u02d1\u679c\n\x83J\x8b\xac\xe7\xb0\xffpr\u00b3o\xc8\x0e\xe1\x1a\xc3\xd6\x00\xfe\x8eC\u0383\x95N %4\xa3\u0673 \x16(\x9b.FO\f\xf1\xdf\x05\ra\xfbn\xd6\a\xcd\xd1\xd36\xce'\x94\xa0\x1ac\xc70\xc3h\xf7\x05\x10\x04U\\\xc1\x9a.\u04cb\xb4T\xdf\r\xab\x01\a\xa5\xb8E(Y \xd9\x01O\x96C\x87\xec\xbai\x1b\x92~\x9c\x91\x80\u01f6\r\x98]3 4C\x82P6]\x91-\x89\u0410\x80\"\xe7\x9e\x19\xb8\xe2A\xbe\x1f\x86\x9fV\xdb8\xec\f_\x85\xb6\xe8\r\\\u01df\x95\xbe\x90'\xde6\x14\x06\x9d\x91zX\xa7\xc2:h\x8f\xa3\xe5\x04E\xc30\x05\x9d\xf7\x90/\x9fw\x9b\x03S\x82\x8d\r\x8c(j\xb7\xdb\u0631}{\x1f\x006\xcdlZ/\xe6\x17\xf21\xaa\x9033\xee\xbd\xf7\xbb\xf4\xe0\x83\x0f\xd0\xd2\u04b2\xbb\xb6\x9ch\xe9\xe1\x17\xba\xbf\x8f\u007f\xfc\x9f\xcaM\x9bf055\u016fz\u056b\xf8\xc6\x1bob\"\xc2\xddw\u07cd\xbb\xef\xbe{\u037f\xfb\xdb\x14r\x00\xb8\xe4\x92\xcb^\xd4\xed\x83#b\r\xef\xf7\xb4\x0f\xf2\xdd9\x9b\x12\x18\x14\xe0e\x06\x93\x02\x87|DO\u007f\v\x85Cx\xdb\u046a/\xa5\xd8\xe9\xa9\xc2\xe3\xab~\xcb.\x18#r-\xeb\xb5/0Z(\xf8\xb7\xc8\x04\xc0\x0e\xd7f\x80\xa8\x85S\x916z.\x95=\xefH\x87Q\xfb.7\x8c\aG\xfa\x13&4\x97\x10~\x1c\u050b\x14:r\x02Dn\xa1V\f\xd4\xc0-L\xc1cE\x9a\xb4`\x8c\x1a\xab\x0e5\xf8\xec\u0395\xe0pzA\xfeu\xe9\x19\x10\x00=&\"\x85e\x94\xab\xef0\xf3&\x8d\x1ce\x8f\xfb#\x11\x1aU\x8f\x9d\xec\x16\x86m\x81\xc3\x0e\xaao\x1d\xa7\u0727jS\xc4\xcc#\x01\xdf\x15n\xad\xc1\x1e[\x8e\x8a\xe2\xf0\x86\x04\vZ\xdf\u055a@Q\xb2\f\x05\x02\x93\x80V@\xdfZ\xe4%\xa3)\x81\x86\x00\xa4\xd7\xe4[c`\xb5\x01\xeb\xb0X\xa0\x1a|ZF^\x18\xf4s\x03S2\x1a\x06P\xa1\xb3\xf0|Q\"\x87\x93\x93\xb7i\x11\xf1\x9d\xaf\"\x9d\xd3l\xec h\x12B@*\t[\x0e\xa0\a]\a\xcf\x19\xed\xf1{a\xa5\xca\xe4`\xd0??>\xde\xf9\x1b\xbf\x1b\xe1\xf5b\xfe\x12;(j\xa2\xe3\xf5d\x9f}\xf6\x19ZYY\x11\v\v\xe7\xbdO\xca\x00B\b\x8c\x8f\x8fcbb\x02\x13\x13\xe3\x18\x1f\x1f\xb7;v\\l.\xf4\xe7\x96\n\x8d\\\xf1\x930\u05a2=\xb5\t\x9d\xa9Y\u75e15\xa8\xcb\x10\xc2\u008c+\x80$\x94\x94h*\x89\x8er\xe2\x96\xdc\x00\xb9q\x8d\xbca\x9f#i\u06337\xb8\xea\xf4\xc2\xe0\x93it\x15O@\xea\xa0t\x8c\xc6Tr\xa8\xa0yqQ\xb0\x9c&\x06\x1a\x03\x87e\xe7\x1d\xe1|Q<\x9f\x8f}g&\x92\x87K\xad\x188\xed\xf6\x87\x02\x96\x85f\xa8e\x03\xd5s]k\xb0\t\x10\x9aGx\x82\x8f\xf6_IU\x9b\xe4\xbbHa\x1cvm\x15A0\xd0\xe8:\xb8\xced\xfe\xb9\xda\u054b\x01\x0f}\x9f\n+\x13K\x97:\xc7|8\b\x9a\x87\"CAn\xd1\xed\xdb\xc8\x1b'c\xe12\xe5\x9c\u0692|\x00\xb2\xb5\x16\xec\xbd\u01a3B3\t\x99p]9;u\xa7\u05700\xb0d\xbdI\x8e/\xaem\xf2\"+\xa0o\x19\x05\x80\x8c-\x945\x10\x9c\xb8$\xa6P\x8e\x05\xf2\u0720;0\xb0\xa5Ef\xfc0\x99\xa8\x06\xd5\t\x1b:\xf2J5\x15\xf8\xe8Lu\xfc\x8c\u00c4]H\a\xcb\x18\x8d\xb2\xb7\x04\u05b9\xeb\xee=\xc3\x06\x04\u03b2\x06\xc6\xc6\xc6\xe6o\xbd\xf9\xc6S\x00p\u026e\x8b\xd6;\xf3\xff\x15\x8e]\xbb.\xe5:6\xf1\xd2<\x94R$j\u05a0.H\xc2\xe8\x12\u04db/\xc6\xcc\xd6Ka\xf2\xbe+D\x85\x81Xf(\t\xa0\xcdh\x801\x91\tlk\x13\xba\x86p\xaa\xcfCb\x14@\xf6-dnk\u04514\xf4\x1dV\xf7\u02a8\u0665V\x81\x91\x80\xf1\xe1\xcfI@M\x10\xda @\x1d`'.bF.\b\xdc\xf0\xde,\xb6j\xbd9\x8apFy\xc2r\xec\xc8\x1d\xf7\x9d\xa1z\x06\xaak \x8a$W\xd2\xfa\x0e\x9b0zQJ\x8a:y47\x9a\x81y0\\\xa4\x18\xbb\xa7j\x8a\x9e\x81\x96\x84\xa2\xe3\x02\xaf\x89\xebU\x9c\xd6\xda\x04\xd0j\u007f\x95\xd5\u007f\xc3\xf5\u074f\u01cdEi\xd1X\xd1P\x03\x03\xd2\xd6\xd1\x04=\x93\x04\x91.h\x00\x1f\xda\xc06\xfcn(\b\xc3\xe3\xe76t\xec\xde\x13\x9c\x03\xe3F\xc03b\x04\xd0\x12Q,Uh\x8b\xbc\xd0 \xb6h\b@\t\x06\xac\x89|sk\x19EiQ\xe4\xce\xeeZ1A\xf9\xd7\xce\x18\x87\x8b+\x12n\u0776\x9e\xba\b\xae\r\xf6\u067f\xa7B\b\xb0p\xb9\xa0L\x02\xf0\xcai\xa9\x14L\x99\xa3w\ue933\x15\xb0\x1a\u0194N\xf0\u0116'g&09>\xf6\xdc\xc6\u0249\xc7\x01\xe0\xdaW]\xb5\u0799\xaf\x1f\x17\xcca\a\x83\x81\xa6\xe4\xc2v_\x0ev\x99\u06b4\x03\xe3Ss\xd0y\x0e\xc1\x16L\x04QZ\xd0J\xe9\xdc\x0e%\x81\xacA\xc9\n\xda\xc2\xcb\xdf\x13\xcf\x13\xed\x04\x1c\x94\xd0\xdb\xc8>_\xe1\x1b\xb6{\xad\xb7\xa1\x01g\x0f\t\xf1\x9cH\x1c\xa9\xb6\x89v\x9445`@\x18\x94\r\x11;\xfapq\xa7\xdd\xfe\xa8\x90y\xf6\x95\x91\xc8q\xdaU\xdf@\x1a\xae\u01fc\x99\xb5h\u018c\x11%tu\xdf\x1e-\xbd9\x0e\x14\t\x0e\xda\u023a.R\xaf\x18\x97\xe0\u0107\x9cj\x03\xe7:\xee\xcf<bm\x1c2\xf5\x8a\xc1\x0e^\xcd\n#\x1c\xcdr\xa9\x84\\\u0480\xb1\xce\xdaW\x9bD>o\xaa\x82\x1e,i\x13\xf1N\x98'T\x01\x13\x15\x8e\x0e\xb6\x91Ji}Q\xb6l!\xfa\x16\x86\x85\x8b\x04\xd4\x1e\x1b7\xae\x1b\u05de\xad\x12\xe4\xf9\x96\x19\xd68\xe6\n3#cB\xe6\xc5d\xde\xe38J\xf2\x03\xccRE\xcbzk\x83\x10q\xe4\a\v\x04\xf2Fo\xce$\x8e\x84\x80P\r\x94+\xf3X9y\b\xd6j\u8c80\u0445\u007f\xad\xd8n\x9e\x9d\xc5\xd6-sG\x88h\x05\x00v]|\xc9z1_?~\xbc\xc7\x03\x0f|\x1f\xaf~\xf5u\xc8\xf3^\xb3(\x8a\rU\u01a8\xb7r\xb2\x16RJL\xcelA\xa62\x94\xdd~\xf4\x90\xb5\x02@nA+\x06F\ttK\v\xddw>&\xa5\xe7f\a\xbf)\x99[\x88\xc2\"u\x06\xac\xb7\x94#@s~\xbei\xf6\xc3_Q\x00\x00 \x00IDAT\xa8\xef\u01bds\x96\x15nX\u82b7\xdb-\xb3\xad{9e9C.\x1b\xe4D\xce\xcf%\xe9\xc4y\x04lQ\xc1-\xde\u042a\xe4\xaa#\xb7U[\x1d\xed\x06\xaa\xdc\xf75\xbcP\xd3\u0166\x8e\x9bG7\xc6Z:}\x80_\x18\x8d\xae\x06\xc0(\xc6\xd5\xd0b\x94@7#\x1f\xaf\x1a$\xc6\xf5\xd1X\x87q\x97\xdaK\xdb\x19$\x01+\x04Ta!\u03d7@\xee=\xe0\r{\x01\x8f\x89\x1e(\xd1\x0f,\xfc\x8c\xab\xc2\x1e\x87\u00fe\x98W\xe1\u02ee#\x8f\x03\bN\xa8\xa9\xa5\x81\xb0\x06Zy\v\x8a\xa8nr\xf7\x19\xbcj\x8cGA\x02\xe2\xa2\x18P\x815\xe4\x1f[\x10A\x06;\x84\x04o\t\"(x\xbfu\xeb\x19.\xcc\x16\xde\u0165*\xee\xc2\x05\x90\xaf\x9c:\x8c\xe5\x13\x87\xc0\u01a2\xcc\xfb0F{\xc1 \x1a\xadV\x13\x97_~\xf9\xa3\x17\xfcn{\xbd\u013d|\x8e\x83\a\x0f\x11\x00\xfe\xcaW\xbe2\x93e\xd9\xcd5?\x17r\x17Tsl\x12\x13\x1b\xb6T\xb8\xa5\xb5`\xeb\xd3\u7640\x9e\x86Q\x80!\x89\xc2\xe3\x00%\a/oDY\xb50\x88\xb1[\xc18k\xb4x\xfe\xf9\x8a\xfcj\xfbB\x0e\xf0\x8bq\x8c\x10p\xc5(a\xf2\xaaD?DT\x03\v@c0\xa1`\x9bT\xc1\x11C\x0f\x1f\x9d\xbcC!\xb7N\xce.\xca\xcad*.(5N>\xaf\x02Uj\xb0C\xba\xe3H\"\xf0j\u05b5\f\x10\v\xe7\xaaH\xd5.&\xeb[\x80\r\u028e\xf0\xe6V\xd5\xcea\xd8J\x97B\x18r\xf4\xff\xae|Gl\xaea\xcb2B# \x02\v\x82*-\xb2%\r10\x91\xe7M!<\u067f\xef\xa9t\x9e\x83\x978\xd8u\xec6\xc9w\xa2Du\xe9\xcdZ\xc2\x10;\x1ac%\xd9~\u04baA\xb2\t\x1cO;\x94\xfb\x17T\xa7\xfe\xf5\x94\xccP\xda\x0f\x9b\x03\x87\u072b9\x11\xe9\x96>\xc0\x82R\x9f\x1a\xe1\ny:\xbf\x00E\xd5.\bP\xcd&t\xd1\u00d9\xc7\xefE\xb1\xb2\x80|\xd0E\x91w\xdds\x90\x02\xe3\x9d1\x8c\x8d\x8d\x1d\xb9\xed\xb67\xfd\xc5z1_?.\x98\xe3\xe7~\xee}\f\x00\x87\x0f\x1f\xbd\xe2\xe8\u0463\x1d\xf6pBhj\x98\r:\x13\x1b19\xb3\x13\xba\f9\x8c\x1c\xa5\xe2L\x02\xa4-xE\xc3H\r\x9b5\xc0\x02\u0436\xa2\x83\xc9\u04b1\"R9(%6\x1a\xb4*d\x99F\xa3\x15D#\xb1aw\xf1Wj\xd1\u062c\x86\x02@\x1c%\xfe\xf0|\xee\x96\xd5\xc8'%L\xab\x12\xca\f\xd3\x0fA\x1c\xe3\xc4D\xe9\xf0\xfeXP\xfc\x13\xa0\xc8J\\K\xca]\xcbMJA\xf2\u06a8\x80\x12\x06M\xa0\x04\r\xc3\xef\xc2\vx\x82\x9f;gT\x9d\nP\xa5\xf0\x04I{\xf01\xf1?'x\xca_a\xc0\xdaT\xe6QB@\xb0\x80\xea\x1a\xa8\xae\x87W\x9cK\xd8*\u07df\x00\x9b\x04\x13\x1c\x8e0\x8bM\xd01Nr\x908v\xc4Q\x1d\xed\xb9\xe3\xae+\xae\x1c\u0365e\xd8\xcc\xdb\xd2&\xbe@\xe4!!cl\f\fQ6\x14*\xaa\x99\x93\xa5a\u0615\n\xb6\x82\\l\xb0\xe9\x8d\x0e\xa3\xae)\x11$\xc1R@6Z\x00I\x9c~\xec\xbb8\xfb\xf4\xf7\xd1\xef\x9eG>\xe8\xc1\xe8\x12B\bXcx\xf3\xe69\xba\xfe\xfa\xeb\xe7\xf7\xee\xbd\xea\a\xeb\xc5|\xfd\xb8\xe0\x8e\x13'N\xbc\xf1\u0631\xe3U\xac[\xa0\x9e\xc1\xa23=\x87\xe9\x8d\x17\xc1\xe6eT\xf5\x81\xc8yZ\x04\xac\xb7`\x88\x15\r\u06f60M\xe1\xb0M\u007fO\"\xe7\n+\x8f\x85kh\x1aGk\x10\xcb\u04e4\xddx\xfb:o\xbb\x86\xbd[$\x9d.\xfb\xce\xd9ue,\xc8\x1b(\x11Ta\x81e \a`Z\"v\xe8<40\f\xb6\xa8j`A%GC)'\xf6\xf4~25o\xd9a\x80\x85VA8\x15\xf0\x9f<M\x9b\x90g\xc2j\x17\xe6\n\x89\xf3\xa5\xb0@\xe6\x19:\u0178\xb3\xff\x05[\xb06Il\x9a\xad\xba\xe6\x14\xdb\x0e\x10\x8b6Q\xfe\x1e\x8a\xac*\f\xb2\x15\x03*=\xbe\x1d}u\xbd\"\u0606\xe2]y42\xd7\x03#\xc2\xebm\x83\xa3\x0eW\xc3\xd0\u040d\x1b\x138\xe76\xd9\x121\xac\xb7\u0115\x02\xb0\xd2\x17x\xebY'\xd6:\f\u076f\u007f\x99u\xc5<\x18f\x85\x05L\xacN\xaf\xf6\x1f\t\xbfK\x10\"\xb0\xde!H\xfa\xbf\x0fb!\v\x92m\x90T8\xfd\xc4}8|\xef\x9dXY8\x8db\xd0s\xaaO\xb70p\xb3\u0664\xb9\xb9\xcdx\xf5\xab\xaf\x8d\u2f75\xf4(\x17\xc2!\xd6K\xdb\xcb\xe3x\u6667B\x91\xe8\xe4\xf9\xe0CeY&\xb6\xb9\xae\x1b\"\x12\x98\x9e\xb9\b\xe3\x93[a\xb5\xae\xb6\xd2\xecq\x14\xed}\xd0\r\x83rvI;%bP\x01iv\x1d\xed\xf0\xb6\x19\x89D\xbfFf\xa1\xb5\x91\x96\xe1\x82I\xb5\xf8\x9f\xb8P\x84\xc7J%Zd\x87\xa8\x8d\u059dWs\xd9@\xe664\xed#G\x97\"\xb7\x8eO\xceU\xc7\u01e9K\x17\x8f\xfa\xab\xe1\xd3\x1fAQ\xe4\xd1{\x11\xb2~\xa0\xca\u4564\xc1\x03\xc5?\xb0a\u023eFv>\aus\xe7\x17b\n\xa7\x03\xd0\x05\u0614\x0e\x13\x0f\xe6U\xb6\x8aV\xe3\x98.\x15\n\xbc\x85\xd0\x16\u064a\x86\xccMe`eS\x88\xc3e\xe0\u054a3\xb8\u05a9W\xde\xf8\xec\x98\x1f\xfe1l\x90\xe6\x1b\xeb\x04?6\xc9\xe9\xf4\xefQTezz'\xb4\x1brF\u007f\xfd$\xbfSZg\x95\x00\xcb0\x9e\xa9B\x00$\xeaA\xe0\x11\xff\xf6\xda\t!\xa4\x1f\xde\x10\x84\x94\xd1\x04(\xec\x84T\xbb\r\xa1$\xce>y?\x9e\xfb\u0397\xb0|\xe60\xcab\xe0\x14\x9f@p9\xa5\xd9\xd9Y\\s\xcd5O\xde~\xfb\a\xff\xaf\xf0h\x17j!_/\xe6/\xa3\xe3\xd2K/\a\x00|\xf3\x9b\u007f>\xf7\xcc3\a7\x17E\xeeY\x01\x0e^\xb0\xc6@eMl\x98\xdb\x05!\x9b\xce\x1b\xa3\xa6\xbeC\x84]\x9c5\x1eCu-\u050aF\xd8\xc1\xbb\xc1\xe7\x90\xe9v(\xe4r\xa8\xa3]\xa5\xe3\x1f\xa2f\xd4\xc484:\x95\xc7:>\xbbH\x1e/\xa6\xf2$\x18s\x18\x94\xca\u0722\xb9d\x90\rx5:O\xa1+w\x1dk\x8a\x92\b[Q\t\xa96~\x1cm S\xa3v\x0fS\xea\xb9>\xf4%\xeb-\x01<\x1f:\xf0\xf1\x85\xb6@Q\xba\\\u0322\x80X\x19 ;\u05c3\xe8\x0e\xbc\x87\x88\xb3\x8e\xb5Z;K\xc7\x00\x97p*\x87\xaf0hf\xebT\xac]\r\xd9\xf7\xf0\x8a\xef\xe6\x899\xc1\xc28\xc2'q\u01d6Z\xce&\x9dz\xbdsw\u007fk\xb5\xf1\xbeE\x1c\xb1\xed\xcaL\x8dc\xda\x12\xc0 c!Jw\xbe\xe4\x87\xc1\xe1\xe5\x91\f(\x1df\x16\x15\xfbH0U\x9e+)\xa7\xde\xffCx\x97S\xe7y \xdc\xe03(B\x05!k\x8d\x81m\x89\xe3?\xf8\x06\x0e}\xeb\vX>y\be\xdew\x8aO\x0e\xc6q\x12\xcdf\x13{\xf7^\x81w\xbe\xf3\x1d\x9f \xa2\x02\x00\xbe\xf8\xc5/\xac\xc3,\xeb\xc7\x05\xb4z\v\xf9\xe6s\xe7\xe6\x91\xe7E\xb4\xbc\r\x95\x85\x84\x84h4\x87\xe2\xba*\xa5%\x05\x01L\xa0\x8d0\xa0z\x06\x99\x00tK@\xe5\\\x85\x19\u0512\u042b\xa1\xd5jT\x85\x86\x18}k\tq\xea\xaaE$\x14\xf4X\xb8E:\u0324!%\xb7\x83\\dn\u0440\x06\x93\x84nz\x8d\xa0\u007f\rT\xe1\x15\xab\x11[q\xf0\x920Im\x16XM\x00\x1f\xb9\xb5\xa8o\xffk\u03c6\x91.3\xee|\r\xbb\x01.\x12Z\xa0\xe7\xbd[o\n%\xb4E\xa6\t\xb6C.B\xcfw\xf6\x8c49\x89+\xd1&\u06da\x8dm\x96[\xa8\xdcQF\xbd\x02?\x89\xacOv\"\xf1g\xc904\xe0\xf3\xb1\x90\aY|\b\u00a6\u02b66\fJ-j\xf0J\x94\xeb\xa3R_*r\x81\x1e\x9a\xaa\xe1\xabd\x82\nj\xe1\xd4R:\b\x8a\x82\xc68}\x9e\xd5\b\xb6\n\a\xf7\x8b\x8c\x90\nY\xd6\x06I\x81\xe5s\xc7q\xe4\x89{p\xf2\xc9{Q\xf6\xbb1\f\u00c5\x9a\x13\xa4\x92`\u02f8\xea\xaa}x\xdd\xeb^\xf7\x1f\xdf\xfd\xee\xf7|\x15\x00>\xfa\u044f\xd2\xfb\xdew;_\xd0\xd7\xf6zy{y\x1dKKKse\xa9\xfdE\x97\x94 \"Xk0\xc8W\xc0#:M\x1f\xc1\xe8qj\xcf\x06\xf0[\xe5F\u03c5\xfe\x8a\xe8Y\xee\xf1KOidO\xfe%\x1e\xeaVGu\xdb\xc35\x91\xab\x01e\xed\a4\fkT\nS\b\xaf\xe7\xf6\u007f\xc7IJ\x0e\x19\x86\x1cX4\x96-T\x81\xc8[\x17\x9a!\xbb\x9e\xc1\x82j\xb8'-Cr\xe2OO\xcf\u007f\xb2\\\u06d2\x8c(\xf3\xf1iT\u07be\u0387D\x83\x8b\x01l\xeeS\xeaKO',5\x10%\xf4\x16\"7h,\x97\xceo\xdc\x06/\x14\x93\xb0M\xc2\xe3\xa4\xee\x86\x16j\xa0\xa1r\xebw\x19\x1c\xe1\xa7t\xf7\xe5\xfcTL\x02\xa7\xd8X<\x81\x14\x8e\xe1\u029b\xc5T\xd0\xca0\x9eN\x1e\u06a9Ac\xf0\xec&[9-\x8a\x84E#\xe0\n\xb9\xb4\x9ez\xe8\xc3]\x04Q\xfc\x8aiP\\}v)\x11\x84!\x0e\xb9%\xb2\xac\x05\x10ci\xe1\b\x0e\xfd\xcd\xd7\xf1\xc87?\x8b#\u007f\xf3M\xe8\xbc\x0f\xa12\x17\u007fH\x0e\x8e\x91J\xc1\x1a\x8b\u077bw\xe3\xcdo~\xd3\xe3\xff\xe2_\xfc\xef\xff\f\x00~\xe6g~\x9a\xde\xf2\x96\xdb\xf8B\xbf\xb6\xd7;\xf3\x97]g^\x850\xd7\n\x8d\x10\xb0\xb6\u0120\xb7\xe8\xd3}j\x16\x84C\x05\x98\x12\xcbU_\xd0KS\xf3\xbd\x88\x05\vU\xe8A\xed\xf1hDXD\x8d\x88\x9d\fCiD\xe7;\xb2)\xa6\x8a\xd1\x12\\\xbah(\x17\xde\xe3\xdf2\xb7h,i\xe4S.(Z\r,dak\xc3Z\xb2n\x0e\x00\xfc(Y\x04#h\x97C;\x8d H\x8aV\xae\xa1\u02e5*\x12-(&#VM\x95\x0fwX\x91d\xc1hXF9&\xa0\x9bb\u0235r\xa8#f@\xe5\xce\xf0L\x1a$\x90T:\u04f0C\xe9=~Qb\x8eC\xc7\b\xa9p\n{P\xb4\x92\x8d\xae\x87\xa8\xbcpl(\xe4\xfe\xef*Q\x16\x92\xa1)A\xb1c\x9e\x18\xe1(\x88\x8a\xadw\x00\xa0\xca?\xc5&Cu\xaa\xad\xe4N\xd1\t\xf6\xbeA~\xc1\x90\x19\u02a2\x8f3G\x1f\xc6\xfc\u0267\xb04\u007f\x04\xcbg\x8e\xc0Z\x03\xa9\x1a\xee:\x00\xc1J\x05\x995`m\u0266,h\xc7\xce\x1d\xb8\xf1\xc6\x1b\x9e\xfc\x95_\xf9\x95\x9f%\xa2%\x00\xb8\xe5\x96[\xf8\xa7~\ua9f1^\xcc\u05cf\v\xeah\xb7\xdbOeY\x06)E\x15\x1b\xe7\xc4\x11`mP\xf4V\x92Z\x1a\xe8p\x14)^\x84\xaaH\x06\x95\x0e\x99\xca\xe6\xd6J\xbf\xe5\x0eb\x11/\xf4\x11\xb6Nba^\vv\xa6\xd1?\xe7\xa1\xda\xe8\u04cdyDkO\u05a9P\x8d\f9\x90ud\xc4\xd5s\u03eb\x16\f\xd3\x10\x90=\xafX\xb5\xc9I\xfa\xe7\x14\xec|\xe3\x02E\xc3\u0388\x8cQ\x88z=\xc0\xc4\x15<\x1b\x14\x96\xa8\xa0\v\x88\n\x1a\x89\u007f\xe8\x17\xb6\xb8\x96Q=\xf8Zj\x06y\xb5\xa8n\x8a\xdaZRQ\x16\x1927\xce\u007f\u0778!v\xf5<\xb8\xce\xebDU`+\xf6\x89\x8d\xafU\u0569W\"*\x8e\v\xa8\xf5b\x1fNT\xa9\xd503x\xa8\xb07\xacw\v\x96\xcb+\r\xcfI\xb2\xc3\xc33\xdfp\xb0\x17\x1a\xc1CI\x91zjy\xd5\xce\xd1c_\x11Z#!Q\xe6=\x1c;\xf0=\x1c;p/\xfa+\xf3\xaeOWM\xa8\x84\x11\xc3\u0110RA\xaa\x06\x0f\xfa+\u0632u+\xae\xbf\xee\xfa'\u007f\xf1\x17\xff\xe1{w\xef\xbe\xf4q\x004?\u007f\x86gff_\x12\xd7\xf6z1\u007f\x99\x1d\u059a\xefl\u06b4\x11Y\xd6\xf0\xb8y\u0569\x1b[\xa2\xe8\xaf\xc0\x98\x12\x14$\xd0q\xe7JUWe\xd9\x19MyN\xb7cdT]\x19+D\xae4\xfb!\x1fE\xee\xe2\v\xb4\xb8<\xe4}\x9b0Ix\xa8\u00a7!\fq\xdc\x19\xdc\x15\xb5ss4\xa2:\xe7\xaa\xe5\xe6\xb8-\u03fa\x16Y\x8f\xeb\x186\rm\x0eR\x1d=\x85x\xbbZ\xd4r\r\xcbO\xb2#+\xfc\x98\x11=Ol\xe899Y\x1b\x12ug\x8a\xabGn\xb9q\x11u\x10\x15\xebD2\xd0\xec\x1a\b\xcb([n\xb7D\\A\"r`\x90\r\x8c{\xaflE\x19\r\x90S\x85.\xa36\u8904\xc1b\xb9\xf2\x19\xb7\xde3%\r\xa7H$\xa2\xfe\xed\xe3jw\x10\x96\x8a`\x9aOT\xb1g\xa8Z\xf6\xd8r\xe5;\xceT\xc9\xf1}\xe1%\"? v\xb4\u01b8\x03\xe1\n\xdc\x12\x14\x18Y\x0e_[8u\x00\u01df\xb9\x1fy\u007f\t\xb2\xd1Nf#\xfe~\x02\x14D\x84R[\xda4;\x87\xd7\xdet\xe3\x93\xff\xf4\x9f\xfc\xc6{o\xbc\xe9\x96\xc7\x01Px\xec\x97\u032e{\xbd\xbc\xbd<\x8e\xa7\x9f~\x02\x00\U00016dfc}qvv\xf3q)e\x92\x14C\xb1\x9b\uebdcG\xde_\xae\"w\x12\x8b\xbd*\u6362\xbc^\x98P$\xd8[\xbc\xfad\x1a\u02f5b\x94\x0eBG\x17\xf1\x14\u007f\x19\xfe9\x8d\xbei\x8aj\xac\xc6x@\x06\x90E\xa5\x10\r\xe7\xe5,\x00\xdc\xffe\xc9P\xb9\xb7\u007fM\xf3\xe2\x14\xd5t\xfeU\x97]\x89cj\xcf\xc6\x17\x13k\n\x18=\x80\xd19\xb4\xcea\xb4\x0f/6\x05\xac)amj\x1f[%\xe7 \xc2\xd1\\1R\x12<\x1b\x9e\xe1\"K\x860\xde\xe1P[\xc8\u0720\xb1T\xa2\xd1\xd5 cb\xa7-\n\x83\xacg\x1c[\xc4\xdfV$y\x9a\x96\x13\u02a2\xef\xc2\x19)\xab%\x04){\xcfr\x9b\xc07IW\xcf\xdes%(b\x839\x96+\x84\"R_\xab]C\xaa\u016c\x16\u02cc\x05T\xf0h`\xef\xcb\x12>k\xe1M\xf1l\x15\x11\xfe/\x84\xf3V\xa1\xa4+\x97\x12\xba\xe8\xe1\xec\xd1G\xd1[\x99w\x8d\x8a\xad\xf2=\xd97\v\x82\x84\v\xea\xd6\x1a\x17\xed\u0709\xf7\xbd\xef\xfd+\xff\xe1?\xfc\xeeo\xf9B\x8e;\xef\xbc\x13ke\x12\xac\x17\xf3\xf5\xe3\xc7z\x84\b=\"Z9}\xfa\xf4\x97\x01\x80|vg\xca\xd3-\a]\xe4\u0765\xea\x02\x1a\u00a9E\x9a>\x13\vd\xbd\x8c\x86\xa81w-\xd2\b~\xf9\x1a\x85\x9c\u05c0\xa1\x87\xb0sZ\x033\xafq\u0343J0$\x01\x05\x9es\u02b6\xb1\x15O=\xdc\x16\xd6eDj\xe9%4f\x88#\x9e\xbc$A1\xca X\x1f\x00lt\xe1\xa4\xf5)4\x118\xd8<\xe42\x98b\xf95\nPb`\x1537\xabs$\xed\xa9\x8b\xda\x15[*-\xb2e\x8d\xac\xeby\xe6\xda\x19\x84\x91\xf6\xd2zckB.$E\x9c\xb9\x12\x04\x85\x02n\xbc\xd0'\x0e@=\xc6]\xa9wiUh\x84e\xaei\xc3\u023b\x18V\x8aYJ0o\x11?\x1b\x10\x02\x12\x14\x13\x80d0|K\x06\u035c\xe0\xe5B\b\xa8F\x1b2kA\b\xe9<\xf9\x93\x0e^\xaa\f\xc5`\x19K\vG=MQ\xc4\xddN\x1c\xce\xfa]\x811\x06Zk\xbc\u136f\xc7o\xfe\xe6o\x1c\u0771}\xfb\xb7\u00a3\xbe\xf3\x9d\xef\xe4\x97\xda5\xbe^\xcc_\x86\u01ee\u077bt\xa6T\x8d\xbf\x1b\x14r\xbap\xd1Y\xa8\rI\xb9\x06\xaf\n\x06\x84\xa5\xa1`\x06\xaa\x15R2\x88E \xd2\xe5\x80\xe7/\xecT\x83\x9e\xd7f\xba\xa4\xb5/\xfa\x9b\xd4\rvca\t]\xb9\xa5\u06a2\x11\n{0\x80\n\t4\xc4\f\xa3\x00\xad\x9c`\x87-\xaf\xe9$\x93\x9e\x905\xa5\xe7*\xdb\x1a%p\xd4*U\x1f\x89\xa6\ue0e8\u007f\x9f\xf2\xb3\xfdW\xe0\x8f\xbb<K\x1b\xbfH[g\u05fbRB\xf5J\x88\xdcq\xcf\xc3b@\xe0$\xe8\xc1\xc6\u0162V\x90\xa3}m\x85\x91\x10\x8d\xe0\xd4sE[\xa4\xa1.\xbbF\u4a7d\x06\x14\xf38\x1d\xc5\xd3\xf9\xcd;\xe4\xcd\xdb,\xf8\xe7\xc5\t\x03I\xf8BO\x00\xa4g\x9f,/\x1c\xc3\xca\xe2\t\xb7\x03!\u007f\vA\xb1S\x0f\x85;\x18j\x11\x11d\x10\x13\x81\xfc{\xca.\x88\x9c\x19\xa7N\x9e\xc4\xe9\u04e7\xce\x11\xd1\"\x00\xdc\xfd\xe7w\xbf$\xaf\xebu\xcc\xfcex\xccl\xdc(\x85\x14\xae HT\xa0\x817\xe9\xd7e^\xebr\xabb\xc6\xd5\x169\xe5w\x0f\tc\xaa\xe1\xa7/W\"\x91\xe1'^0\xa3\x8a4\xa5\x83<Z\xa3cO\xb1t\x1e\u075eW1g\x1c\xe3\xce(\xca\xed+\xa0d\xb8\xdb\x176\x14\x15D\xbb\x80a\xae;'-\xa8\x83V\xb4Sd\x06\xe5$\x0f+A\xa9f\x80\x85\x11i7\xa9\x1dT\xcc\xe6Ko\x1b\xbf\xb5U\x97\x19^\xaf\xe0\xf3^22\xe3\x1d\f\x93\x1d\x87+\xd4\t[\xd3\xff\xb7\xe6G\xce\t\f\xc1\x0e\xd7G\x12\xe5VS\x8a&&\xeaL\x15N\x9e\xf2\x86\\\x13/\"\xf6\x1en\v\x0f\xb7\xc5b\x9d2k\x02\\\x15\xfc\xc7\xc9Q\x14\x89\b\xb2\xd1\x04\xdb\x12\xc7\x0e|\x0f\u01df\u074f\xd6\xd8\x14.\xbe\xe2\xf5\x98\x9e\xbd\x04\xe4\\\xdd\\\u0291)16\xb1\x11[.~\x15\x9e{\xe2\x1e\xe8\xb2\xef\xe4\xfc\xc2\x17\xfd\x00\u01d0\v\xc6&b|\xef\xaf\xef\xc1\xefg<\xcd\xcc3D4\xff\u05b7\xbc\xf5%y]\xafw\xe6/\x93\xe3\xfe\xfb\xef\x8d\xdf\xcf\xcf\xcf\xeb\xb2\xd4U\xed#\x8a[h\xaa\r\x1c\xab|E\x1eQT\xe3P12\x18\xeaTF\xc1\xf5\"\xbe\xa6\xce&)\x00\xe9u=\x12nI\x19\x1f\u00eb\b\x11\xb8\x9eP\xba\xeaN\xa8v\xfbp\x17\xf5T\x1b*\u0639\x16\"2\xf7\x86\xa0\x960\xb830\xba\x841\xa5\x0f\xce\xe3j'R{\xd2\u0460v\xcdn\x1d\xa8\v\x8a\xe2\v\x91\xc2\xf2\xb18'L\x98p[\x1f\x80,4 KD\xa1\x13\x12(\x87\x13;,\x8e9\xa9\x9e\x85\x14\xbab\u02f0\u01b1n\xac1\xee{c\xc0~\x97\xe2d\xf7u\x1ac\xf0\u008f\xe2\x1e\xcf\a\x8f\x9e\xf3\xfe\u70bcca:\xe6\xb0u\x8b`\"\x02K\t\b\x01\x05\x01)\x15\xb2\xe6\x18t\xd1\xc3\xc1G\xbf\x81\x03\x0f\u007f\r\vg\x0eb\xe1\xf4!\xac,\x9d\x02I\a\xb3\xd8\xe8\xa9n\x905Z\xd8u\xd5\xebq\u054d\xef\xc7\xc5W\xbe\x0e\xd3s\xbb!\xb3\xa6\x87\xc0l\xdcG\x18k`u\x8eS'\x8fb\xff\xfe\xfb\xf7\xfd\xfb\u007f\xff;\xbf\xf0R\xbe\xc6\xd7;\xf3\x97\xc9\xf1\xb9\xcf}.~\u007f\xe2\xc4\xc9+\x8b\xa2p\x17\x90\xa8\x04@\xbe\x1f\x8a\x98fZ\xfdhT~dr\x1b&W\xb8\xb9\xc6Cv,\x05\xf6\xdd\x15q\x02\x81\x8c\xf07\xe7\xc4X\x8bW7\u0135`\x86\x91\xb5\xbe\x96\xe2\\7\xa6eJ`\vB\x8d\xeaG\x89\x1a\x95\x85\x8f\x1f\xd3\x1e/\x0f\xf7FTu\xdb$\xc0!\x8d\xc6\x18\xdf\xf5s\u0176\xa1d\x87\x92\x9eR-]\x82\x86^N\xaa\xbd0\x9c\xa4,E>w\xb4\xa2Mp\xf7\b\xb3se\xcb\uba60qTKCV\xbdT1X\xd2\xf3\x8c\xbc\xf7\xf8\x8es\x92\x9b\u0249i\xa5\x0f\xd3\x18\x9ac\x04\xdex\xfc>\xb0A\x98*o\x14f\xefxY\x050\x13\x10#\xfe,\x18\x86\x80\xa6\x90h\x90\x04\f\xe3\xfc\xd9g\xf1\xdc\xe3\u007f\x89SG\x1f\x8a\x9d\xf6\xd4\xccE\xd80{i\xf2\xdcS\x96\x92E\xa39\x8e\xed{\xae\xc7\xdc\xc5\u05e0,{XY>\x8d#\x8f\u0743S\x87\x1ev]:\t\x94\xf9\x00\xf9\xa0\v\x80\xb1\xb0\xb0\x80o}\xeb\xdb\xefd\xe6\xdf'\xa2\xe2\x91G\x1e\xc2\xd5W\xbfb\xbd\x98\xaf\x1f\x17\xd6\xf1\xd8c\x0fc\u07fek\x00\x00\xf7\xdc\xf3\xad7\xfc\xd6o\xfd\xf37\xe5y\xee/t\x91\\\xb6\f\xa92\b\u0568\xbalN\xbd\xbb+\xd3\xff8\x90d\x97\\\u00de_\xee\xac\aQ\xf3\x05!\xeb xJ\xf0\xea\xb5\xfc\u0369V\xd8\xeb\xf8\xf7\xea\f\xcc\xd5\xeeU4\xac\xbe\x8c\x9cw\xacR\xfe\x04?tG\xa3s\x85\x9c\x05\"/\x9d\x86r\x90+*\xa4\x81\xb1%\xac\xd1\xd5\xd9\xc7\x13\xe6zd\x1b\xf1*J\xe5\xf0s\xae\xadp\x91\xceX\x9d;\xa7\u06e4\x14~\x11\xe9\x16\xdb\xdf6P3\xadO\xeb\x11\x04\x96\u038dP\xd8*$48\x15F\xffr\x0e\x83lJ\xe8\x96\xee\u0770\x81R\xe8?\fb\x84\xb7\x0e{\xf6\ty\a\xcd`\xaf\\\xadU\t/?\xf8\xa5\x84\xb5-\fO\t\x10J\xa23\xd1F\xc3\x02\xfd3\xa7q\xec\xc0~\x1c{\xe6~\xac,\x1e\a[\x83\xe6\xd84\xb6\xef\xbe\x0e\x17_\xf9\x064\xc76@\x1b\x97\b\x94f\xd9\x02\"F\xd7\t\x99\xa1\xdd\u0684\x89\xd9\xed\x18\x9f\x9aA1\xe8b\xfe\xf8\x01W\u030b>\xac. \x84\xc0\xe2\xe2\x12\u039c9u\xe5\x9dw~\xf5\x16\x00\u007f\xf9\x8do|c\xcd}\xe4z1_?~lG(\xe4\x00p\xf7\xddw\u007f\xfc\u0631cY^\x14P\xaa\xe1\x99\x03\x14/\xeaFk\x1c\xad\xb1\xa9*\xf1<\u059dJ\x91I$\x12\xf1\x90\xff\x9d\xf2L\x84P`5\x83J\vmC\xf8\xf2*,au!g\xd4\xc53k^M<\xac\xea\xc7\xda\x0e\x8c\xf5\x01f*:r)>\x04\xf6\x90\x04\xab*/\x94=\x16\r\x93\fm}\xf2<\x9b\u0485\x1a{\xe1I\xec\xa4y\b\u02e7zi\\\xf3\xd9$\x90IM\x12\x13\xc9\xdau\xef\xf6T\x94D\x02qH[\x87\xa3\xb8\xf6\u007f\x8a\xef\x1dy=PJ\xd7\xf34D\xaf8\r\x81\x0f\x15\x86\x9e<\xbf\x04.\xa2\xaa\xf7O\x05\xa7q\x19\xa0!\xc0)\x8a\u0390\x84H\xc0\xc7\xc4\tBc\xac\r\xd9\x16\xc8\xcb\xf38v\xe01\x1c~\xe8\x1e,\xce\x1f\x81.\xfbPY\x1b\x1b\xb6]\x89\x8b\xf7\xbe\x1e\xb3\xdb\xf7A\xc8\f\xba\x1cT\x8c\"F\xa2\xf8\r\x90\xa1\x1bp\x9ab\x00A@gr36\xcc\xed\u00b9\x13\xcf\xc4YG`\xc1\x94e\xa9\x1b\x8d\xe6\x96#G\x8e\xbe\x1d\xc0_\xf6\xfb\xfd\xf5b\xbe~\\X\u01d93'0;\xebl;\u007f\xf0\x83\xfdo\xff\xc4'\xfe\xf9O\x9e=;\x0f!$\x93\x10DB\u0536\xf3\xad\xce\x06\xb4;\x1bk\x8c\x860\x8f\v[k\xf77\xbe\x83\x16\x04\x92@&\tR\x917\xa9fP\u18fd4\xfb\x82>T\u022b\xfd\xfbPGMC\x9d\xf0j\xf8\xbb\x12\x86\xd7d\x93@m\x81I1\xee\u0564\x9bXtd\xf5\x05fp\xe1=kb\xec}T\xb3\xb8A\xa7\xd5\x15?;Q\xb3\xd6 \x13\xa2\xd50\u03ea\x85\x86*\xc9<W\x81\xc8\xe9\xb3\u4d10'\xf8yjW\xe3\xd6\\N|\xd0\xfd+\x14\xd7%\x02Y[\xbd_\xc1\x13%\xb0X\xac\xad-\x85\x9c,0\xd5\x0e\x01\t|bk\x8b\x10\xa7p\xcf(\x9f_\xae\xa3[\xa8\x911\t*k \x93\r\b\xa1\x91\x0f\xce\xe2\xd4\xe1\x838\xf6\xf4\xf71\u007f\xfc\x19\x14\xfd\x15\x10\x11&\xa6\xb7a\xdb%\xd7a\xc7e7\xa339\v\xads\xe8r\xe0\xeeEP\x9c\xcfT\xf7\u02c8\xbc\x19\x87\xe1@@\xa2\xb7|\x16K\xf3\xc7\\T\x9c5\xc3\xef\x8a-\xcb\x12\xa7O\x9f\x19\x03\x80s\xe7\x16\xd61\xf3\xf5\xe3\xc2:B!\a\x80/}\xe9\xcb\x1f=x\xf0P\xa3\xd7\xebCe\x19\xa5!\u0396\x9d\x05\xee\xe4\xc6\x1dh\xb4\xc6at\x19\u0558iWK!7\x11\x95\x9b\xa2R\x84L\x02\x94\t\xf0\xa4t\xe1\x15K\x1al}\xea<\x11J\xc3q\x98:*h\xad2\xac\xad\xeb*k\x1du\xf0\xf7`\xc2\xc8\x14\b\xc2PU\x1dz\x9c\xa1\xa2.\bPd\x80\x16\x81\xdb\x04\xd1\xf79\x98p\xae\xb2a\xb1r\u007f\xc70TQ\xfa\xa2w\x8d]#\xab\x82Fd\xd4\xf1P\xe7L\\\xa3\b\xa6\xb84\xa7.\x88\x9c.\x1a\xd5+\x13\x99$\x82`\x85w\x17\xf41w!e\x87\u0233\x8a\xac\xe3\x82S\xc26J\xef8\xfa\xb9\x87$\xb7\x14\xf2\xaa1U\xfcp\u04cf|\xeb)\u054888\xfb@\u620bK\xbf\x9b#\x01H\t\x91I\x186(V\xcecq\xe1)\xac\x9c{\x16\xa7\x8f=\x8e\x85S\xcfA\xe7}0[t&7an\xd75\u0636\xeb:L\xcf\xee\x01\x84DY\xf6\u076e(\xb2\x96\xc4\xd0\\b\xf5\x9a\"d\x06\x06\xe3\xf8\x81\aq\xee\xe4!\u05cc\xb0M\x82\xcc}<\x9d\x94\x90R\xf8\xef\xc5z1_?.\xac\xe3\xfb\u07ff\x0f\xd7]w\x03\x0e\x1cx\xf2\xda_\xfe\u53fc\xfa\u0529S\x90R0\x91 \"\x17f\xcb\u0582\xb5Fkr\x16\x9bw^\u5de7v\x15\xb2\x11\xe8fQ\xf2.\xdc\x00UJ\x82\x10\x04j\x10\xb8-\x80\x16\x9c\xea\x10\xec.6\xed\xca@\x99fa\"\x8d\xfa\xa9\xa3\xe1\\A\xab5\x98\xb8\xda\xc0s\x95(3t~\xb4\xc6\xf04\x14\xbf\U0003d400\x94\x06R\x02\x18\x03l\a \xc9`\xcd\xe0\x9c<\x06LU\x19\xe3\xba#\xa2\xc3\u05a9V\xc4R\f\xbf\x86\xb1\xc7\xf3I\x02\xd6<\x0f\x9a\x92\xbf\x8b\u007fS\x1b\x84\xa6\x88QB\x89\x1c\xde\xd3\b\x00\x8a\xa2\xd25.\x8b>)\xca\xd6\xc47\x1c9\xe2\xd5\x1e \xb1\xb3M\x9eD\x05\xb3\xd8U0\v\x00_\u0607\x86\xcc\xe4\x8al\xe0}\x93T`\xab\x91\xf7\x97\xd0\xeb\xcec0X\xc4`\xb0\x84\xc1\xca9\xf4\x97Nc\xf9\xec\xb3\xe8.\x9e\x811\x06D\x02\xad\xce\x04f\xf7\\\x8d\x8b\xf7\u0744\xd9\xd9\xcb!\xd1F\xbf\u06c3\u0579\xf3\x9d\x17\"IY\rV\xb7\x15k&\xce\x1a\xbc\nU\xca\f+\x8b\xa7p\xf4\xc0}\xd0\xc5\x00\xaa\xd1r\xf1yBF\x16\x8e\x14\xa4\xb2,\u00e6M\x9bN\x03\xc0\xf4\xf44\xaf\x17\xf3\xf5\xe3\x82:\xae\xbb\xee\x06\x00\xc0\x1f\xfd\xd1g\u007f\xe6\xf4\xe9\xd3[\xbb++Ve\rA\xe1B\v\xc3(v\x91q[.\xfe\x89(|\xa9\xa9@\x13\xa3\xadt\xa8%\x04A\t\xf7\u007fd\xe4\u0094\x05\x01S2F\xac3\x18\x8a\xad\xcb\v5\x95!\xd3\u8ac5V\x99$2\xd7\xfb\xf6\x80\xe9bx\x00J\x18\n\x19N;K\xf7#K\x00\tF&\x18\x92\x18\xdc\x04\xb8I\xee\xfb\x16\xc0\xe3\xe4\u0515\xc6\x0fD}A\xaf\x89z\xbc\x83!%])s\xe54\x99\x92\x13+\xb7\xc6\x04\x13\u786e\x9b\xea\xdd\xf1\b\x14\xaaF+$$\xb3\tA\x90\u0085X\vE\xdew^\xban\u0738\x9d\x86\r\xa6VBx\xa5'\xd5\x17\x87\U0003ace3\x1e\xa6\xf1l\x95\x1a\xb5\x1e$\x1d\x19$\u0273$\x92\x90B\xc5\xfd\x85\xd1\x03,\x9d=\x8c\x85\xb3\x87\xb08\u007f\x18\xfd\x95s(\x06\xcb(\xf2\x15\x94\x83\x15\x98r\xe0\f\xb7T\x06\xa9\x9a\xe8LOc\xf3%\xfb\xb0u\xefO`\xe3\xae=\x18\x9b\xd8\b\xbd8@\xb1\xd0s\u063a\x10u\x1dAt`LX5\xbe\x90W#\x1e\xb7\x8b<s\xf41t\x97\u0382\x84\x84\x14\x02\xa0\u0327\x12\xb9\x1b6[m\xa1\x94Z\xb8\xfa\xea\xab\xef\a\x80\xad[\xb7\xadw\xe6\xeb\u01c5s\xfc\xf5_\xdf#n\xb9\xe5u\x96\x99w\xfd\xfc\xcf\u007f\xe8\xa7O\x9e<\t\xa5\x94!!D4\xd8b\x97\xec.T\x86\xb9\x8b_\x89\x89\x8d\xdb\\|\x96\xf5\x85\x83\xd2B\x85j`\x1a\xb6\xe5\xd2s\x95\xa5\x005\x85\xb7je\xa0)\x80I\x99\x10\xb5\x9d#\x1e\xb3\x830\xb8\x16\xceP\xa1\x9d\xab\x80\xedT9Z\xc3\xdc\xebc\u0151~H~\xa2\x1ay\xe1\xfe\ue534\x90\x12 \x05`\x8c\x00U\xc1%\xd4\x06`\bb\x89A\x85\xb3f\x85\x00\xac\xf5FV\x94\xac0\x89\xf5\xe3*\x1b\x8f!\x8b\x82\u0618\xdb\xcab\x96\xbc\x83`2MD]\xd2?\xf4|\x89jCG\x10AH\x82P\x80Pn!\xa5\xb6\x04\x8dI\xa0g\xc0]\xed^y\xe9\x829X\x00\xd0\x02\xd6$+Qub\xf5\xedP\f\xb3N\a\xae\\-\\\xe1y\v\xd7\xf9\x86\u075c\xd1\x05\x06\xdds8}\xecQ\x9c9\xf6\bV\u039f@\u007fe\x1ey\u007f\t\xd6\x1aW\xf0U\x13*k\xa21\xbe\x01\u0371\x0e\xa6\xb7\\\x84\u037b\xf6b\xd3\xce\xcb0\xb6a3\x1a\xed\x0e\xac1(z]\xf7\x1e5\x04l\xe1\xc3:l}\x98\xbb:\xc9))\xf4\xfe6\xba,0\u007f\xf2\x80\xeb\u02b3\xa6S\x89Z\x1d\xbdc\xb4\xd6\u0632e\x0e\x13\x13\x13\xfbo\xb9\xe5uw\x01\xc0\xa5\x97\xee^\xef\xcc\u05cf\v\xe3`f|\xe8C\x1f \x00\xf8W\xff\xea_\xfe\xe4\x93O>\xb9\xf7\u0739\x054\x1aM\x05\x12\xae\n\x03Q\xbe\u0759\u070c=\xaf|;\xa4j\xa2,\xfa5\x17\xabp\u0344n>:\x0f\n\x17\xc5\x05\"\xa0\xe1\xbejT\xb4\t\xe5\xfc7\x16\xb5\xdf\x0232\xaf.\x8c\xb8\xf7*`\xbc\x8e=\xd7\vI\xa0\xb2\r\x03\x19\xcf\x17\x0e]\xdd5\x03P\xc4P~\xe0\xc9m\x80[\x89\"2\xc0\x1b\x1dw\xfe\xbc\b\xd8\x02\x10R\xb8\x8a\ue855\xe8\xefB\xe9yP\x05]$\xbcm\xa4\x8cE\xae\xf4\xa7\x18\x82\xd6\u04c4{\f\xdd\xf5j\xa6\x8a{>R\x02J\x12\xa0\xdc\xc2$[\x12bB\x82\x1b.\x9c\xc32\x83z\xda{\x98\x93\xe3\xfb\x03\xd0\xcc~qJ^_\x1b\x98\xf2\xc2u\xc1CR\\w[\x0fn\xb1\x05\t\xef\x03n\f\x06\xbdE\xe4\xfd%t\x97N\xe2\u0729\xa7\xb1p\xea),\x9d;\x86\xbc\xbf\x04!$\x9a\xedILo\xbe\x04Yk\x02\x8d\xf68:\u04db0>\xbb\x19\xe3s[0\xb1i\x0e\xad\x89\rh\xb4\u0690B\xc1\f\n\xe8\xfe\xc0\xc1t\x1e\u02b3\u02a9\x88a*\xb7E\x11\xb8\xae\t\xf54\x8d\x90\x8b\x88\x94\x90(\xfb]\fV\x16\x9c\xddB\xd6\xf0\xe2(\x1bEf\u030c\x99\x99Mx\xfd\xeb_\xf7\x9d/\u007f\xf9\xcf\x00\x00\xb7\xde\xfa\x86\xf5\xce|\xfd\xb80\x8e\xaf~\xf5\xcf\xc4\xe7>\xf7\x05\xc3\xcc3\x1f\xf8\xc0\xed?\xfb\xdcs\x87!\xa5t\xad\x91\xc7\n\x9dO\x87\x01\x83\xb1\xf3\xf2\xd7b\xeb\xaeWE'\xbf:\xd4\xe1\xbaRA\xd2\x0fL\xfd\xc6\xdb\xc3+\x9cB,i?#\\A\x87\xf7\u0486e\b\x16\xc8\x04P\x94\xb6\x8a.C\xbd\v\xac\xb3X\x86\v\xf6\xa8\xf8\x9e\x14?\xe6\u06b6\xbb\x86\xf9\x13 \x84\xf7\x0fo\x00\u0726J\xab\x13 \x8fP\x14\xc6\x00a\b\xb4L\b\xc4\x1e\x04\x9ae-\x0e\x82W[\x9b\xd30F>\xb4 \xd10\xb0N5f\b\rC\xed\xb12U\xab\x9f 89z\x80X\x1a\x02\xd4\x11\xee}\xb0\x8efI\x1d\xe9\x1ds\r\xc0\x06V\xfb\x9cSE\xd0\xc6\x11:\"\x84\"$B\xb5w6\xb4aA\xb2\xa8\xf3\x87\b\xa4$L\x99cq\xfe9,\x9e;\x823\xc7\x1e\xc1\xe2\xd9gQ\f\x96\xa1\x8b>\x88\b\xad\xb1\r\x98\x9e\u074d\x89\r\xdb19s\x11&gw\xa1=\xb5\t\xcd\xf16\xb2\x8e\x82\x1c\xcf \x9a\xca-\x1a\xd6\xc2\xda\x12\xe5\xa0\v\u03ad\xe3\xbb3`u\x12&\xad\x84O|\xaaXU\x11+G\x02\xbf\x90\x83\xc6\u020b\x94(\u0495\xfc\x1cDkg\xfef\xc3\x02l\xd1\xe9\x8ca\u02d6-\xa7?\xf2\x91_\xb9\xeb\x1f\xff\u33fdd\xaf\xf9\xf5b\xfe\xbf\xe8\xf1\xd0C\x0f)\x00\xff\x1f{o\x1ak\xd9u\x9d\x89}k\xed}\u03bdo\xac\xc7*\xb2XU,\x92\")Q\xd4\u040eE[\x92GIv \xc5v\xcb\xee\x8e\xed\xb4\xe1\xb8\xe18v\x8c\xfe\xd5\b\x108H#\x06\xd2H\xfe\x04n\xa3\xff\x04H\x1b\xe8\xd8\b:A#\xed\x8ch\xb8\xd1vK\xeeA\xb2-Y\x94<\xc9\x16\xe7\xa9X\xa48W\xd5\x1b\xefp\xce\xde+?\xd6\x1e\xcf=\xaf\xa8?,J\uaec1G\xbeW\xf5\xea\xbd\xfb\xce;g\xed\xb5\xbf\xf5\r\xcb\xdf\xfc\xcd\u007f\xfc\xd1\x1b7n|\xea\u06b5kh\xdb\t\xeb\x8d\u0369\xb8\x88w\xd8\u07bb\x80\xf7~\xf7\xdfD;\xdd\xc1r~\x98\vbY\b\x99\xf5\x81\x8f\x10\v)F\xab\xc5\x04\x90\x86\xebf5V\"\x03\u040e\x85\xf49\x1f\xd4B\xedG\xbb\x90\xbc^\x0e\xcfV(\x84i\xf0\x9a\a\x82\xe5\xc00\xe7\xc1Q\xfd\xba\x87\xfe'\x00\x98\x04lD\xef\xfa\xa9n@\xa7A#D\x00m\t\f\x04r\f\xf4\xc1\x00\x8a\xca\xd3\x01\xc9\n\xf7;\xa6\x1d\t\x95HF1<-R\x84ra\xafs8+\x9c\xbc\n\x80\xce\xc1\xc4\xd62\xd8\xe8\x06\u028d\xc2+\x98h\x91J\xa7\x83P\xd0\x01\xc5\xcf)\xf8\x92\x13ge\xae\x16\xbe\x12_\x96\x14\f\xa1\xd7Y\v\xa5\xf8 \x02b\x1df\xbev\xf5/\xf0\xf8\x9f\xfc\xbf8\xb8\xfeu\xb8~\x01\x88\xc7ts\x0f\xb7\xdf\xf5~\xdc~\xe1!\uc77f\x0f\xbbg\xef\xc6\xc6\xf69e\xdb@@-\xd0l2\xcc\x16\x03\r }\xa7\x18}\xe2\x9dS\x86\x92\xa2A\x1a\x91\xdaB0\x81\xac(\x05\xb1\x18\xb2f\xedZ\x91\x10U\xda 3a\xba\xb1\x8d\xe9\xe6^\xf0\xac\xe9\xe1\\p\xe7'\x86x`\xef\xcc\x19\xdc}\xf7\xe5\xafN\xa7[\x8f\xe8I\xf6\u007f\xc0\xaf\xfe\xea\u007f\xb7.\xe6\xeb\xf5\u03af\xaf|\xe5\x11|\xf7w\u007fd\t\x00_\xfe\xf2W>\xf1\xf4\x99\xea\x13\xc8\x00\x00 \x00IDAT3\u03c0\x98\x05\x14x\xe5D\xea\x9c\xe7\x1d\x98\x19\xef\xfb\xf0O\xe2\xe2}\x0f\xa3\xeffy\x00V\x15r\xed\xdc\xc8T\x92C\x18\x0e\u0130\x86A\rUP+S\xd1\xcd6\x04:cUH\x14\xe4\xe2\x16\x04\x0f\xc0\xf5\xa8\a\xa2\xc3\xc6{4_ndh\x1a\xe5\xf9\x05\x8e-\xf5\xe1B)\u0261\x90\u01ee|\x85\xdaX82\x1a\x06h[1\x12\xdaw\xe8\x8d\xc2\x11zB/B\x1a\u00a0\x14\xa1\b\r\x1b\xf6\x8c\x9bS\xfawCX%\xe7\xaaJ\xadlM\x15?\x98\u007f\u015f\x83\x94\x92H\xb1\x90o\xf0hl\xaaX\x00\x9b\f8\x06y\x17NC\xf1D\xe5\xd5\xe1WrPC\xe5\x99P\x9c\x1c\x88c\xf8\a\xc1\v\xe0\x9c\x83\xb1\x13\xdcv\xc7}\xd8\u0739\x03\x1b[g\xb1w\xfb}\xb8\xfd\xd2C\xd8\u07bb\xa0\x9f\xe7z8qp\xbd\a,\xd0Z\xa3\x8c'\x13\xbdd2K\x88\r\xeb}\xd4\v\xa4\xf3\x05\xaf\x9e5\xb8\"\xa41I\f:\x19\xccLjQ\x1b\u00acD?\xb6\xed\x14g\xef\xbc\x1f/>\xf3\b\xfa~\xa1\x0e\x8c\xc6(\x13\xcb\x1ax\x11\x18c>\x1f\xbf\xde\x0f\xfd\xd0\x0f\xad;\xf3\xf5\xfa\xe6X_\xfc\xe2\x17\t\x80<\xfa\xe8\xd7>\xf8\x0f\xfe\xc1\xaf\xfd\x9d\x97^|\t\xd64\x94\xbc\x9d\x83 \xa4\xef\xe6\xb8\xe7\xa1\xef\xc7C\x1f\xf9\x8fam\xabX\xf9\x00\xd0 \"\x80M\xa2qA\x02\x9b\u00c4\x87\xc5\x10\xa4e\x90\xd1b\xee\x11\xc2p8\x1c\x00$\xccD\xa7\f9cA\u0483\xe6\xc5@\x14\x8a\xdf\xc2K\xee\xfa\v||\x05J\x11\x19M\x9a\x1b2\u05c7\xaaO\"e\xaf\xc0@\xa9\x93v\xc0F\x17\xacl\x1a\x04Q!\u0536\x9ao\x91\xf3\xe8\xbd\xd2\"\xc5\xc5\xf4$\xaa\x10\x15B\xe9\xa8(\x15B2\xf4a_\xd9\xc0bT\x1c\n\x91\x10\xd5\x1b\x9b\tTP\nb-LI\x8b5\u05db_4\x10\x13\xaf\xbf\x00\xda4\xba\x19\xf4\xda\xf12\v`\u0507\xdd;$.}}\xa4 T\xd9\x12\x1e\x1aDa\f.\xdc\xfb\x9d\xd8\u067b\b\xb6\r\xa6\x9bg\xd0Nwa\x9b\t\xbc\xeb\xd1-\xb3\xa0\u01c7\x13L\xd32\u0314AS\xbdW\xe0\xc3=\x02B\xa1A\x03&\x16\xd2\xf5jY\x1c\x94O\x86\xd4A\xd1#\xde\\a\x04\x1cB\xc9K\x83\xaf\x94PT\x84\x8e\x13[\x9c\xbf\xfb\x03h'[X\xce\x0f\xe1\xd9\x06\x91\x11\x81\x8d\x85\xf7\x82\x83\x83\x83\x97\xe2u\xfe\xbe\xef\xfb\xc1u1_\xafo\x8e\xf5w\xff\xee\u007f)\x00\xf0{\xbf\xf7{\xdf\xfb\xd8c\x8f\xefz/`c\x82%\xa9\x16\n\xd7-\xb0\xb1s\x0e\x1f\xf8\xfe\x9f\u0159\xdb\xefA7;JY\xa0\x89\xb1\x1d:\x1b\xf5\x9b6\xe9A\xf7&C,b\t\x98P1\xac\x8bc\xb4\xc21/\u05ba-\xa3\xb8\xb9\xd3\x02\xce\x00\x1aa,{\x9f\xf5\x9c\xd1#DFb\xd9N\xe9\xcaSP\u0100\xa0Q\xd6H\x06\x94\xb52!\u0224TK\x16\xa2\xf3\x01\xa1\xa6\x82+\xb6\t\xa6S\x0fqu\xc7\xd59\x82/\xbdfB\x11A\xc1\xd7N\xe3N\xaa\x1b\xffj\xc7\xc9f39^u\xf8\x93\x87\xc2jX\x8b\x1f\x19\x807\x8c\xce$\xa2\xc7L\x04g\x06\xf2\u007f\x91\x00-mi\x87\x8ec\ar\u0280i\xc0\xf0,\xf0NB\xfe\x85$oxfJz\x83d\xe4\x15\xbe^;\xddQ\xa5\xb0\bDz\x888t\x8b\xe3\xe2\x17NI9k[\x86\x9d2h\x83A\x96C\xe0rqc\x14\u061c\x18\x81\x01\x01\xc7\x0e\xe8B\x188\xeb\xafN\x85gT\x18\x8a\xd1\n\x8bI7n^\xb1\xcc\u07fe\xed\x12\xb6\xf7.\xe0\xe8\xc6\u02da\x9c\x14EV\xe1\xb3\x1ak7\xbe\u055f\xfb\xb5\x05\xee\xb7\xd9\xfa\x97\xff\xf2_\x84\x1a$;W\xae\\\xf9/\x9ey\xe6Y\xbd\xb9\xd9\x14\x93\u007f\xf5\xa5x\xe0\xe1\x1f\xc1\xbb>\xf0\t\xb8\xe5|0\x95C\xe2iG\u0447\xb2XD\xbd\xa8\r\xa5\xc1\x1b5\x19{.\xc2\xe5*\xa8\xa1\u00a1\xb7-h\u01c0\xac&\xd3Y\xab\xf8\xafX\x827j\a\x10\x1d\nW:p\xa9;\xefa\xd7.9]l\xd0\xd5\v\x98Dq\xda-e\u007f$8Z\xe4-\x1d8\b\x00M\rh\x87aZ\x85_\x8c\x91\xa0\x9f\na\n\xac4F\x89S\xc9\xf25\x0f\x8d\xbeb\xa8\x03\r\xaf7F\x15\xf1\xf1\xd3\f\x87!3\x11\xa8a\u040e\x05\xb5\\\\\u3890\x17j\u0304E\xb7\f\u07b1\xa0M\x03\xb2a\x98\xc9\xfa\xc6Q\xf8\x15m\x84\x933\x1a\xd5v\xbe\x01\xaf\x12\xf1\xe8\x963\xf4}Td\x02d\x03\x14\x17\x18NB\x80i\x18\u0366\x85\u0659\xc0n4`\u00ea\x06\xb5\xe1\xcdpbIqp3\xa4\xa9\x05m5@\xcb\bLX4\x86\xd0X}\x9d\xe9\x0f\x83N\xa2\xcc\xe9L\x14ZP\x8e\xad\x13\x81\xb5-vo\xbf\f\xa2`\xc2\x15\xec+z\ufc71\xb9\x81\xcbw_\xfe\xd1\xf85\x9ex\xe2\xd1ug\xbe^\xef\xfc\xfa\xb1\x1f\xfb\xb4\x00\xc0o\xfc\xc6\xff\xfc\x1d\x8f>\xfa\xe8G\xe6\xf3\x85\xe2\u0764Gj\x88G\xbf\x9c\xe3\xf6w}\x10\xef\xff\xf8\u007f\x8a\xb6\xdd\xc2\xe2x?\xc9\xfa3\x9dC\x1f\x06f\vf\x9b;\xd8\u062d\x87B -\xd7\xee}A\x15\xba\x02\x8f\xc4V\xd9\x00\xb4\u06c0;\x81\x1c\xf5\x00\x01\r\x03\xbd#t^s;\x99bp\xef@\x918\xa8v\xa9\v.\xc4D\xa5\xadJzM\xa4\\k\x9a\x12dR@\"e;\xef\a5\xbdpK\x04\xd4\bJ6\f\xa4\xf3`q\xc0\x12\x89\xfb\xdd\u01df\xcd\xc7\xe0\x03_\xbd\xbeX\\\x93;a9\xa0\xa3\u06b7\x86V\xc0{\xfd{f\xe8\xc0\x93\x01\xb2\x04l[`\x83\xf3\xcc`\xe0\u03d2,y9t\xdaqc\xdd\b\xf7\x02\x11p\u0483:\xc0\x04\xa1\x97x\x01;\xc0\xc5{\x05\xc55\xaa\xc2@\x94\x11\x14y\xde\xe5\xe7\x88d\aCb\xc0n2\xecn\x03\u07b6\x01v\xf3\xe9s*\x9c;\xfe\x9c\xf1\xf7\xb8\xc1)@\x9c;\xbdn\r1:\x84\xccQ\xcf\xf5\xb6^\xbc\xbe\xa4\x04-~\x99l,6\xb7\xcf\xc6<\xbb\xf0m\x19\x86\x8d,\x17Kz\xfe\xf9+\xf7\xc5\xcf~\xef{\u07ff.\xe6\xeb\xf5\u0373n\xdc\xd8\xff\xf9g\x9f}\x0e\xbd\xf3`\x13\x8a\xb1wp\xdd\x12\xa6\x9d\xe0\x81\xef\xf94\xce\xdf\xf3\xd7\xd0]?\xc8~\xe65X\x1e:&[\x17\xbd\xb2\xe0\x9b\u0615S\xf2<bR\x15b\xb6?\x1f\xd0\x15=@-\x01{\x8d\xd2\x15O\x1c\x98\b\xad\x12\xe8\xe0\xbcv\xd1e\x1aN\xe9\x12\xb8\x12\xe1F\x85\xd0\x05\xb9(\u050dd\xe8\xca7\x90\xbar\xfd\x11\xe4\xf4\xa6|$\u308d\x81l:H\xef5W\x14\bC<\xc0\xa5\xc0k\x19\n+Q\x99\x98\xa4bB#\x93WZIgC(\x8a&\xce(X\a\x9e\xbcm\U000b597cAD\xf5\xa3G\xbd\xd1\xe5\u0670\x80\xa6\x06\x1c\n6\xc1\xab3d:-\x10\xc8\x01\xceyxY}\x99\xb5\x0fM\xf8\x1e\x1c\xac\x94\xa3\x8bd\x80?LK\xb0\x9b\x16\xbci\xc1\x8d\t\nS\x14\x02\xab\xbc\x19\x94\x9e\xfa\xf1g\xe2\x8d\xd0\xe1\x1f\xf7\x9a{\xea\x152\xf7\xac\xdf+\r:#C(\xb8v\x0e\xdc\xdb\xe1\xc5\u00d0A;\xdd\x0e\f\x16\x97\xe6\n\xc6Z\xda?8\xc0\vW\xae\x9cy\xe3\xcdW\x1f\xbe\xfd\u071d\u007f\xba\x86Y\xd6\xeb\x1d_\x9f\xfd\ucfca\x0f4?\xf9\xd4S?x||\x92:l\x1f:\xa2\xbe[\u0dbb\x1f\u0103\xdf\xf3\x13\xc0\xa2\xd3\x1b;\x03\xc4\x05\x19$\x14r6\x15\xd0+\xa1\xa0p\x80Yb\x8a=\r\xa8e\xc3X\xb8\x12;\x87\x00<e\xd0\x19\x85\t@j\t\u0432\xf2\xa7}e#@5\x94]\x92-\xcaa(Q\xc6\u024bM\x89\x10\xe0\x90 j*_S\x15\xeaSvx\xa7\xe4\x93\x121\xc8\x1a\u015e7\xf4\xe9a\x12X\xf2`)\xfc\u06c7P\t\xa12,+\x1f;\x19\xc0\x03\xe5&\x15\x83\x1b\f\xe9|\x81\b\xe0\x96@;\x16hy\x00-d\x8c\u0757f_e\x88\x04EN6@\x13\x06\xef\xb4\xc0\x94\x03\xe4\x15\u0330\xc2\xef\xd6\x04\bD\x8f\x04\xfa\x96r;cV]\x80\xef\x925\x04\xe7\x8fMc`7,x\xab\x01\xb56)\x87\x13\u0155\xb9\xc6\u0283\xc3c\xfa\xb9)@0\x1b\x06\xb4\u0640\xad\x81\xb1\f6\n\xc7\xc4\xe8\xb9\xe1p<\x8a\x9aT\fW\xd8\xf7\x82`L\x9bN\xa0\x89E\xc3\x06\xcbE\x87\xe3\xe3\xe3\xf3\x9f\xfd\xccg?\xb2\xc6\xcc\xd7\xeb\x9bb}\xf2\x93\x9a]\xf8\x97_\xfd\xb3w]y\xfe\xf9w-\x96]!\xae\x10\xb8\xbe\x83i'\xb8\xe7\xe1\x1f\u0199\xdb.C\x16\xf3\xccb\x18\x14\x126\x06ll]d\x8abI\x1c\xba\\\xa3\x85\x86\x8b\xbc\x8a\x8cs\x94\x82}AM\xd2\x06h\u04c2\xb6\x8d\xb22\x88\x14\x17\r\"$g\x02k\x86\x8atw\u0501\xd0BE\xcd\x1a\xc3\xd0\xe3\xa7F\x06KS\x1f0\xe4\xe6)n\xa3x\xbd\x16t\x026\t4\tu\x8e\x04\x86\xfc\u02b06a\xe2\x05\x9c\xa0X\xb2\xa4\xacO\x12*\xb0qZ\x11Gq\xf4^\x89FZ\x9bV\a\x89\xf1$P\xfdz\x02C#}\xa5\xe8\xac\x18_{\x1e\xae\x82\x00\xda`\xf0\x96\x05\xb5F\xf1k\u03b3\x106\x14\xe0\x97\xf0\xcb5\x8c\xf4BH\v*\aK\x88\x14\xb9\xcc\f2\x06\xc6\x1a4\x13\v\xb3\xd9\xc0l\xd80|\xcf\u007f\x1fo\x16b\x0e\xf1r\\\x17\xf1P\xe8%Z\x18l\x06\x8b\x82\xf0\x12\xac\r\xc1D\x91\xdaH\xa5\x1dqpk\x1c\xf0\xf9!\x9a\x0f\x1a\xff4\xde*\u030c\xdey\xf4\xde7\xcf?\xff\xc2G\xbf\x95\x9f\xff5\xcc\xf2m\xb8\xfe\xe8\v_\xfc\xfe\x97_~e\xbaXh\x8a\x8a\x84\xecH\xd7/\xb1s\xe12\xee\xfd\xceO\x80\xe6\x0e\u03b9\xcawE\x12\xa7<;\xca\r\x1a\xdd\x02/\x0f\xbc`\x1at\xddT\x97RZ\xa9\x94\x89\x12\x016\xa4\x96\xb9\x9d@\x8e<X\x80\u01aa\x19T\x1fC!\xdcX \xe80`\xb9\x00*\xa8B\x8a\xc0\x10P+\xca+\u720d\x13\"\x1a$\xa5\x0f\x8a\xd4\x0e\xb6+\xdf\x0eH\x91c\xd2x\xc8V\x10=-\xa0\xb4GR\xb8\xa58\xe8h\xd5q\x92\x82\x84\x13\xb52\xa1S2j\xa1\xab\xff4\xe6f\x86a\xf3\u0504\u036f~q\xa5\x97x4\xc0*\x8d\u0232QV\xe8\xe0#\x0ee\x00\u06b2\x80\x10\xfc\x89\x03z\u0162M\xf4<w:I\ue4f5{\xb4\xfd\x95!\x8b?\xfd<\x86\x01\u06f0Z\vl\xea\u026b\xa2\x8d\x06!R\u029a\xa5l\xa2V\u0307Wn\x19\xda \x88c\xb0\xf8`\xf5\v8O\x9aGZ\xde\x04!l\x9c\x06\x9e\xc7\"\x1e\xce\xf5\xa9\u0448\xb4Q\x02\x81\r\xcb|>\xa7g\x9ey\xfa\uce98\xaf\xd77\xd5z\u9957\xbe\u007f\xd9\xf5\u4703\xb5MrA\x04\x11v/\xbc\v{g\xee\x06\xf5.p\xbb\aC+\x10\x88,\x88l\xfd0\x84NX\x8a\x937l\u044d\x97\x14\xc1\xdac0\xb3\"D\x8a\xce=\xc0\x11\xad\x01\x9d\x01\xc8\xf5\xc0\xdc\xc10\u0436\x04\xdf\t\x9c\v\xc5\xcc\xc5H\xb3REIE\x1d\x91\xaa\xb0\u01d7\u0361+\x97i\x80YJ\xba\xa3\xc4\x01\xe5i\x8d\xb8r\x9b\x87S\xd1\x14\xceA\x81\x92\xe9(\xe1\xb9`\t\\\xec\x02\xad\xa6\\\xd0\v)j5\xac\xab$\xb0\x85\xf0\x89)\x0e\x94\xa1*\xcf-\x03\x9a\x98\xf1}\xad\x84\x8f\n\xd7\xc0\xe0\xbb0\x88p\xe3\xfc\xc9\r\x01;z\xba\x92yNZ\x82S\n)\\\xf4r\xa7`\xc7 u\xb5\xcd\\J\x18#\xb0\x96\xc0m(\xe4\x1bF\xf5\b%\x9eE\u0671\xb1\xe2\xe1\x17\x1b\xbfO\x03J\xaa\u007f\x86\xcd\xf0\xfe\u072b\xc8\xc8\n\xd0{8W'\x1f\u9f55sGc\xb7\x1e=g\xe25\x97p\x0fXki\xff\xc6\r\\\xbfv\xfd\x9c\x88\x9c!\xa2\xfd\x17_\xbc\x82\u02d7\xef]\xc3,\xebu\xeb\xd7s\xcf=\x93\xde\u007f\xf5\xd5\u05fe\xcby\xa78y\xf4\xb6\xf0\x0ev2\u0145{\xbf\x03\x1b\xb2\tY\xf6%\x18\x8c\xa8\xf2ccal\x9b\x02(\u02beZ\xe2CK\x81Qa9w\x81\xc3\xe69=\x9a\x94\\\xaeJL=w`\x02l\x18p\xc0\u03c95~nbT\xae\xee,\xc17\x04_\bKd\xb8I\f\x11\x96\x88Q\x13@-TTc\xb0\x12G\x97\xbc\xba\xc3?\x12\x1a\xd6\xc6\xf1b\x1f\xad\x83\x01(\xa3d\x8b\x14w6\x04k\x04\xcc\xd9\xd8K\xaa\xe8\xf9A\xcb_2:\x06\x17\x90\x89`\x8cZ\xb4\x92e`\xd3\xd4\xe2 \xe44\xfb\f\xe7 \xd1\xf1rVk\xf8\x1a\xc4a\xf3\xaa\xbf?\x91\u0489h\xbb\x01O\x03\xb5\xd00\xb810\xad\x01O\x14\xff&\x13\xb2D9JP9\xd3\x14I4\xe8\x83\x19l\rx\x83A\x9b\xac\xf3\x14\x19$TQ~\xbf|\x8b\x94\xc5\xfcu\x91\x86\xa2\x11\u07e6hO0Q\xe1\x11\xd9p3F(\xa8\xbc\xc4\x15\xa1\x8a\xaa\xa0\xf1\xca\xd0\rj\xc6utt\fb\xbe\x0f\xc0\x87\x00\xe0\xf5\xd7\u07e05f\xbe^\xef\u023a\xef\xbe\abW\xb6\xf7\xc2\vW\u03dc\x1c\x9f\x80\xd9$:\x9c\x17\x8fv\xba\x85\xdb\xefx\x00\u0187\x89>\xd5\xdcp\xf5\xe0j@l\u01cbL\xc0\xaf\x99\xa2\xb1\xd6*\xfb\xf0\xadJ!\xd5\xe54Uu\xda2\xcaA\xb7Z\x9ct \xaa/\xc3\xd9\x10\x18] \u00eb\x1b\x87\xac\xf6\xd6F@\x93\x80\x95\xaf\xee3yc\x10\xacJ\xe8k\u06eb\xb1\v\x11pb@6\t\x98\x86\x82\xde\x00l%3z\xa2\xb3\xdf\n!>^\x02Z\xb9*\x89SN\x01^\x99\x04\u0738\xc9hx\xe5\xea\x1d\x8bc(\xe4\x1c\xb0i-\x949\xc0\x99\xaa\x01c$4\x86\xdf\xfd4\\\xff\x86\x03\xb4\x13\xe06k\x14S7&\xa8\x81\x8b\x90\x0f\xa1\x84n\x18CZ\x90\x1bV\\\u007fb\xd2\xd03\xfb\xa5P5\xac\x15p\xa0\xbf\x9a\xb4A\x12\x13L\x84\xb2b\xfaR\xe9\xb7b\t\xb4\x11 >\xa3\x9b\xbcg\xe49Da\xc2\xe5\v\x8fx\xf5{\xf1\x15\xbb2\x0f\xe7\x19^\x04'''\xf4\xf5\xaf_em\x88^]\xc3,\xeb\xf5\u03ae\xc7\x1e\xfb\u068f\xccf\xb3\x8bG\x87G`2\x15M\xae\x99la{\xe7\x02\x98\fzY\x1d\xd61\x99@c\xc4j\xe4\x19B\xdeg\x1c~6\x14\x98\rC|\x93*\xc9=Um8*\v\xaf\xe8\xf3\xa1\xd1g\x02\xda5 \xe7A\a\x02t\x82V\b\x8e\x80\xa5W\u0699\x0f\x18xY\u0487q\xc9\t\x8ae\xa8\xa9V\x18|\u0280\x8e<\nU\u0230\u04e7U*\xe4\xe04\x03\x0f\xc0\x00\xb2\x05\x90\xf3\x80\a\x8c\xd5\xef\u0457\xedy\x84#\xdc\xc0\x18jU+\x14\nr\xf0'o\t\xbc\xd5@6m\xf2G\xc9\xf8u\x86L\xa8\x18\xb4f\xd8K9\xe6\xc3@'\xa9\xcfMH\x86/\x9b:\x90\x95\xc3^\xed\n\x90\x19C\u0300'V\x8f\x14\x93\xbd\xcdI\bM\fy0\xca\xc5\xf7\x1bV9\xf1\xc1\xdd\x11)\x12/\x02!A#\xccT\xec\u0152](\x83\t\"\x93\xfa\xf8\x88\x1f\xd0=\x1b\x06m\b\xfa\xce\u00c5\xfb2\xfd\xb6$x\xd8H\u212aj\x19\x9a\u16b7NN\xf7\xae\u0386\x18\xf3\xf9\xdc_\xbf~\xc3\x01\xc0\xfe\xfe\xfe\xba\x98\xaf\xd7;\xbb>\xf3\x99\xcfto\xbc\xf9\x86O\xe6LI\xfc\xc1\x98n\xecbs\xfb\x1c\x04\x94p\xc9\u071eS\u8fb8\xe0\xe9\x16\x85<(\xfa\x88C\x10\x82\xe5\x04\xfd\xd2J\xdfM+\x01\xbe\xa9#\xa5<\x1a\x95\xd2\xdeV\xb4H\u040e\x05\xf7\x02\u007f\xd4\x03 L\x04\xf0^\x14r\x8e\xaf!\x16\x04\xa2\x15\xe7\xd9\n/\xb7\x9a\"T>\xd72R\x98\xa5\b!\x93A\a\x1f\xf3\x96\xb8\xf4&g\x02\xb3\x81\xa7\x10\xec,\xba\xb9\xc9V\x18v\xf6\x801\x1e\xde3\\\fS\xe0\\\xf8U7_\b\x8a\x90\xf3,\x05\x019\xe0\xe0\x86\xb8\x99;f\n\x1bB\x19\ueb1b'UB\x19*\x87\x9c\x83\xbc\xeb\xf2\xf7J$U:\x121\x03\xdb\x04\x11\x86\x9c8\xf8N@^\a\u04d0\u0e98X+jUkC\xd2\x14\f\x01S\x03\xbf\xd3\xc0Y5\u01f2\u0308\xe1@\xbe\b\xf3H\xf4H\xa2\xc2D,w\xe1\xe2)Q \xc5\xfb\xc2q2of\xae!,\x1a\x86\xef\x04\x86\x00\x98`\xb3\xdc\xfb\x94\xad\x9a\xad\x90\x87\x16\x00\xf1\u0102\x90\x96\xc5pT\xf8\xd9\u007f\x8b\xaeu1\xff6[\u05ee]\xbb\xec\x9d\xdfr\xce)%,\x0e\xc4\xd8`\xeb\xccyL&\u06c5\xaa\xa6\x18>E\xc9?h\f3Q8\x81\x00K\x8aU\x92-\xfcP\xca\u007fB\x92\x1c\x04W\x02%GQ\xc8:\x81\x81&\x04\u0675\xa0Nt \ua056\t\v\tG\xe4\xb1/]\xd2\x16C\x81#\x16\xd0\x14\xc0d\xe0\x0f.uC\x9e\xde$\xabH#\xc9\xd0\b%\x9c\x99\xc8\xe7\xa86 ][\xefz\xc0\xa9<\x1c\x13\x026\x058Vq\x8b1\x02/\x9c\xaf\x9f\xc4\r\x82s\u0403\x97\xca\x13\x87)@\x16-\xc1l\x1a`\xa7Qs\xaa\xb2\xe8\xca`JP2\x8a\n+\x81X\u0529\xa2\xeaQ\xb1\u007fsm\xbb@\xba)\xd1n\xf8\x1d\xfa\x1e\x0e\xac!\xd7>\xc0\"\xb1\u007f&\x85UR\xfek\xc3\xc0N\x03lZ,\xc2\xe7\x9b \xed\xd7Z+\xd9Q2\xfe\"\x88rP4\xfb\xc0Z\xa5\xa0\fR2M2{C\xc1Z\n\f\x16\xde`\x18\x11\xd0\"\xdc\x1b\xbe\xb8\a\x92\xd9X\x0e\xa2\x06e(\x87\x8a\x98\xb9H\x87d\xc3h\x1b\xc5\u4b35\xebb\xbe^\xef\xecj\xdb\xf6\xfb\u06b65}\u05e15&\r\x19\xd9\x18\xec\xec]\x80m\xa6)\x99\xbc\xc4Q#\x15QF\xea,R\xd2{\xe8\x1a\x83\x8fv\xd5\xecq\x8d\a\x8fR\xd7V\xb6\x8a\xf0\xa0E\xf5_(8\xbc\xc1\xf0g\xacv\xb2\v\x0fk\xd4\u072b\x8b\xec\x12\xa6D\x8e`PEnI\xf4?\xab\x99\x9ed\u01af\x93\x14 \x83T\xddyP\xe6\x83 \xa1S\xa60\xc43\xe5\xcfE\x04\xb6\x16d\x18\xd2;x\xd7\xc3;\xa71t^\x80c\x82a\x817\x02\xe79\f\xf1\xd43\xc0s\xe0f{\x0fr\xb1#\u0542n\r\xa3i\x18\xbc\xc1\xe03\x16\xbc\xdb\xe8\xbc \x9ap\x15\xfcI*v\u04c8E3\xd5\x13\t\x891uq\x00(\x12h\xdeC9g\xf1\xab\x9a\x10d\x97!\x0e\xe0\x99\x03\xbc\x0f8|d\xbaxx\xca\xcc\x1e\xa5MZ\xf0N\v\xb1\xa4\x8c\x17P\x10\xf6Pm\x89<<\xaf\x89@\\\x1f\xd8'\x94n&a\xc9x\u007fiw+y\xd87\x99\x90n6\u0783:=\xec\xa4\xcfd^\xb1\x1b\x8a\xf1v\xe5\xecE@\xc9\x17\x06\x02\xf4\xce\xe9&\xed\xfd\xba\x98\xaf\xd7;\xbb\xdex\u336d\xae[\xe6\x1b?\xdd\xd0\x06\x1b;\xe7\xc0\xa6I7j%V\xa1<8\x1a>\xdc \xc03\xa5\x00g\xb2\x94\n0\x15\xc1\xc9\x15\xd7;\xcd6\x87A\xc0\xa3\x8ft\xe6\x1b\u01c7u\xdb\xc0\xf7\x1e\xf0\x1e\xa6#\xb4\x81M\xd1G\xfc4\f\xf1\xca\xf9\xa1\x14\xf8\x88L\x83|\x1f\u046av\xc0,\t\x0f\xb3:\x05ReEK\x10\x98\xd0\xfe\u01df'\b\xdf\xf5H_\x15\xd40\xf43\f\xea\x1c\xbc\xef![N\xa9}3\r\xb7p\x88~\xe7\x04\x11\x82k\x00!F\xb3\xa0\xe4?\xa3\xc3F\x86\xb5\f;50\xb75\xa0\xbdF;^\x19\x04\x8aV]y\x8e\xf5\xe3\xf0;\xf4\xa21\xda\x10\xc0;\x1f\x92|\xbcF\xa5En7\x97G\x15Z\x9ddo\x1a\x18a\x00K\u0639\x83a\r\x87\x96^\u0099\xc2k\xd7m\x83\xe9\xd7n\x03\x99X\xb0al\x98\xb0\xf9\x15\xc3Wp6\xf2RA\x1aC\x9c\x87\xeb:\xf4\v\xc0K\x9f|\xed\x01\xa3\xd4W\xf6\xf0\xec\x01'up\x93\xa4\xfd\x1c\xd2\x12\xfc\x86\x81\xb8\x1eB:\fe\x9fKu\x05c\x19S\xb8K\x04\x8fv\x11\x90\xa8+\xe8b\xb9\xc4k\xaf\xbd\x06\x00\xb8~\xfd\xfa\xba\x98\xaf\xd7;\xbb\xae\\y\xa1\x9b\xcd\xe6 c\xb2\x98\"\x14\x8b\xe9\xf6m0\x8dM\x8eq\ts-\x1b\xe3T\xa4\x8b\xee\x95\x011*\b\xe1\xe8\xc7\xc2\xf5\\\x8f\xa2\x87t,:e2\x83\fl\f\xa5<\x0e\x0f\a\x91\x94\x9eV\xda\r\xdd\xf9A\x0f\xb6\x04\xdb\x10\xbc\x10\xbc\x93\xdc\xd1sV6\xa6\x8e\xb3!\xd0.@\xad\x9a\u056e\x84^\f\xe4\xf6\f\t\x12\xf8:\x9b\xb3\xe6\x8c\xc8\xf8n\x15?\xd7\x18\x10\x1b\xb0cx\xeb \xe4\xd4\xfeu!\x80c8\xd1\v\xe6\xa1\xd4E/\x00,\xa7\x94$b\x8bfb\xd0N-\xec^\x03>\u05c2[^y\xad\xd5V\x1bX2D\x92\xe4\xfe\x01\x91Nv0\xe2<|\xef@\xceA\u0125\x81\xacZ\xd3\u02c8\xd50EZ7\xb0c\x000x\xbf\x83_zH/AD$ \x16\x9de\x18V\x13\xad\x9d\x06\bE\xdaZe\xa8\xb0QX\u00c7MS\x88\xf3\u07c7\x1d\x9e\xfb^}\xf0\xbd\x878W\xa8\x889X6\x0f:\xf3\xf8\u07c2\xa7\xefZ\u00b2!\x15=\x05\x02\x8d\x06\x92K\x1a\u019b\u05a0\x99n\xa9E\x05\x96\x85\x1f\f\x87a\xb9\xc5l\xb6\xc0\xd3O=\x05\x00\xb8z\xf5\xc5u1_\xaf[\xbf\x8e\x8en`{{\x0f\x00pxx8Y,\x16\xc9\xfa3\x1d\\\xd9`\xb2\xb5\v2\r|\xe7R\xe1\xaelk%&\xf6He1\xeb\rrWn\xb4=MRy\x1a\xcf\xdb\x04rr\xa4\x10*E\"\xc6R\x85\xa4,\x8e1}\x99\x81]\x8d\x9cC\x0fp\xaf\xa2\x94N$\x85\x1e\x10)\xe4c\x88B\xbe\xa7\xe6\x8e\u04b6W\xc1P\x14\xf3T\x91?\xf5\xb9\x80\x13\b/\xc1\x17\xab\x84\x85\ua23a\b\xbbTXsa\x88E\u0702!\x90\xd6\xebP\xee\u0423\xe9\bB\x06\x10\x81\xf3:\xa8sN\u09aa\x18U\x81\x92\x01O-\xb0\u05c0no`&\\c\u0660:\xfc\xa2\x00\xaa\xa2\x9a5\xa3\xe2\x94^c\u0083\x9dQ\xab\xda8\x88L\xd6\n\x18\x19\xfceoq\xec\x1a\b\fx\xbf\xd3a${\x15\xeb\x10\xe0\x9d\xa0\ac\xd94h\xd8b\xc2\fc-L\xdb\xc0X\x930q&5P\xeb%\xe6MPr\x824\xb6\x81\x99\xb4p\xddB\x87\xf2\x92g/Y\xa2 \xd9\xd5R|u\xa3\x91\x10\x9c\b\x16\r\xc1t\x04\xebD! .|\xe6\xc3\t\xaa\xd9\xdc\xc9\x16\x15\x05\x04\xc3`\x881X,\x97\xf2\xecs\xcfy\x00\xf8\xfa\xcb__\x17\xf3\xf5\xba\xf5\xeb\xf9\xe7_\bl7\xf9\xe0\xdf\xf8\x1b?q\xff\xf1\xf1\xf1@_/0\xb6A\xbb\xbd\xabFQ]_\xc3\u0525'Hx\xd0S\a\xc8HF\xfe\x86\x82\x80\x85)\x15\xb5\xe1\xfc\xb3\xf4\xe5\xa6*2\x1d\xab\xdcu\x1a\xa0\xe8\xd1\x1a\x15E\xe5l\t\x14$\xff\xd4{M\xa0q*\xe36\xa1\x80F\x95$\x99(\x80a\xf5\u0096^;P\x89\x015\x8a\x1b\xfb\x01T\xa2\x98\xb5T0\xcb\xe0\xf2\x9d\xba\x04y\xa0F!\xb0\"\xf2\xb8\xe5L\xa0\xd9\x1dQ\xb2\xa1\xd5z\xaa\f\r\u01c07\x04\x17\xd2vx\xdb\x02\xb75\x10\x9b\xb3<\x93\x87\v\rLe\x86\x1cK\xca\xd2~\x19\xd9Y\x89\x19\x863w;n>\x88CXHE\xc9D\x80\xfe\xb9\x05\xc4z\xf8n\tYv\xf0\x8d\x16Vo\x04\xd6\x03=\x04\vg1\xeb\f6\xc8`\xbbi`Mvt\xf4\x14\xe5\xff\x04\x1b\xae\xbb\xf3\x81\x9c(\xba_\xab\u0372\x81\x90\u02ef!\x89\x89\f\xbcs\xf0\xe22\xfe\x0f\xa0\xbc\xb5,\x01mC\x1a\x9d\xe7\xe3\xf0V\xed\x94)\xa9\xac\x18\xcd\xc66\xd86I,\x86\xf4;\x03\x96\xcb\x1e{{{\x97/]\xba\xf4\x9d\x00\xfe\xe0\xde{\xeeY\x17\xf3\xf5\xba\xf5+\xe2|o\xbc\xf1\xdaE\xe7\xdc^\xd7u\xa1\x13.\x86?\x93\t\x9a3\xbb@k!\xb3y\x91?\x19y{\xb9\x13\u00a0\x9e\xa6\x0e\x86D\x85B\xb6\xb6\xb6\xae\n\xb5\fA\x81S\x99\u06ab5\x92h\u0624\xeb\xc7S\x03\xd9\x16`\xe1\xc0>\x04\xfcz\xa5\xfaq\u8b09\x95\x93M\x93 \xb3gh\xaa\x0e\"\x05/|\x8b`l\xe5\xbd\u2fa9\xb8\x95\xa6\x89\x95\x81\xd7\n\xf912,\xb3\u0425\xa0\xbfQ\x12\xc4\x04\xdcgK\xb9\xe5\x98\x03\xe8\x03\x17=\xc0I\xd6k\x98\x05\x87@l\xb3caZ\u029bd\xe1s#R\xb2\xb43\xa3e%K\xf4-\xaetR\xe1\x06Q\x90\x1eMdt@\xcca\xb0\xed\xbd@Z\xb5\x11\xe6^\xe2\x88R\xf1i\x02Z\a\xb8%\xe3\x18\x84\x1e\x0e;\x04lNM\xa2%\xe68<\n\xac\xa1B\xd0\xe3\x01O\x04\t\xc2!xWX\xff\x1a0;xb@\\\xb5\xe9\u01efG\xa4\xf3\x14\u00c0\xb7\f\x0f\xa0w\x02\xe9}\xa2gF\x05\xa9m\xa60M[\xfe\x84z\x12\x15\x0f\xd7\xfb\xe5t\xb2\xd1z/\xef\a\x006\xf6-\xb6\xf1u1_\xaf\xb7a\xc5a\u0375k\u05fa\xf9|\xee\xca\a9\u0791\xa6ia67 \x13\x86\xb7ZX\x84Vs)s6X\x9d\xab\x99\x1a\xee \xe7\xa6\xc1\x103\xc16\x95eG\xe9\xad1,\x16\xe3E>\u05dfh\xea\x14:\xa8-\x03,-\xd8uh\u0200z\xafy\x96\xe1\xb5q\xb4\x9emIa\x00\f\x92|JhB\x04\xc6\x10\xc4S\xf0\xad\x11$\"z\xf5\x1a\x86\x83G\t1zA&?`\x84\xac\xd8\u007fK\x88\xd5\xdb&\xc0yH'\xa9\vN\x9d\xa1Wk\x04\xbfe4\xceN\ua409lD%\x95=\x80\x14\x97\x89\x90-\xd2E\xb0\x1a\x9e:\f\xc5NB&_|-\xca0\x93\x14\xa76\x0f\xc8\xcci\u0232!H\xe7\xe1\xbd\x16cb\r\x880N0\x99{\xcc-0\xef\x19\xfd\xb1\xc3\xc2\x03\xdbSFk\x92\xa7b\x15=\x17\xe7\xaf\x1e\x80\xf3@\x17a\xbeP\xec!z>\x122\xc9z7v\xf8\xd5\xcc \x88\x8d\x98\xf4t\xc5\x1b\f:!,\xe7\xe15\x16n\x8a\xb6\x9d`\xb2\xb9\x8b\xa3k/\x17\xf7g\xc0\u052d\xa5\u00e3c<\xf9\xd43\xc7\x000\x99L\u05dd\xf9z\xbd\x13\x98\xf9\x11\x00\xe0\u018d\x1b\xe8\xbae8FF\xa3\xa1\x10\x1c\x11d\xfa\xde\x00\u0796E\x83\x82;\x1eV\x83$R\a(\xb9xq\x0e\x83(a\x1a\x06\x06f\xe2\xf5\xb7\xa8+I\xc1/\x1e|>\x86\x1bD\xa8M\x86\x01\xbfc\xe1\x9d\xc0\xec\xf7\xabX\xbd\xf7@\x17^\x9f%\x15\xb8T\xa7\x05\xa9g\xac\xb1\x9b\x17\x0e\x02*JA\x13\xa9\xd8\r\xbcu\x89sDY\xe93\xb3\"\xfb/\xc25\x88\xf5\xf8/\v\x01\x0e]\x1d\x8c\x03\xa8\xc8\b\xac\u0664!\xb7BLm\xcd{Z{(\xc5\xe1\u01e10L\x93\x02\xf2\x1a\xfb\x9d\x8e\xbc\x17\xb7W/\x929\xf7\x80\xb2WN\x1c\xd0I*\xe2\xce\xe7\x0e96\xf5\xb2\xf4h\x16j\xbb\xe0\x9c\xe0h\xee\xb0\x14`\xa3!lZ\xed\x9e\xe3\xe5\xe4d\u02dc\xb3F\xbd\x10\xe6\xbdn\xca\rS\xa2\xcc*\x13\x86\x835E\xf9\xeb\x94<\a\x15\x9f\u007f7.$\x19\u016f\x11\x93\x87\xe0a\x9a\t\xa6\xbb\xe7B7\xae?K\x14\xd5\x1acpxt\x8cW^~e\x02\x00\xff\xf5\xaf\xfc\x8aH\x05\xfb\xad\x8b\xf9z\u0742u\xe3\u018d\x00\xb3\xbc\x81\xbe\xef\a\x1e\xe4\x91\u007fl\x82]\x88\x87\x04Iv|\x9e\xcd\b\x9e]i;$\x91\v\x92\x89T\x8d>\x04\x18\xa0d\xa9\xe0\x14\x0f\xa9\x9a\x8e\x81\x9aK!\xa3]z\xb2@\xb7\xcap\x91N\xc0\xfb.\x83\vqo\x12\x01\x16\x02,=\xc4\x0e\x1aT\xa9Y\x119\x9c(gE\n\x17\x8a\u039c\u021c\xb22A\\Y\x11\x94Cd\x91\xb2\u04e3\xcc4Ah=\xbd^w\n|\xca\xc4$\x89\xb5v)\xf0\x8d\xa07\xd9>\x9c\a\x9e\xe5\xb2\n\x85\x87\xce\x14\xd5\u0303\x8b\x80\x90\xe8\xdcX6\xe7\xe9\xc7K\u009d\xdc\xf5KTkF\xab\x85\x99\x83\x9f;\xb8\xa5\x87[xx78\xa8\x84\r\x9f\x1d\xd0\xcc=\\C\xe8'\f\xf1\x82e\xef\xd1y\u00bc\x03\xb6Z\xc6\u0532b\xe4\xd1}\x92\xf2\u0463i4lZzI\x14E\x15\x101`\f\xe0\xbc\xe2\xe1\xc80\x95Gq\xd0\x11\x00\xbd\xc0\x1d\xab\x8b\xa2\x04\xee\"\x89\x86\x85;q\xa0\xa6\xc5\xf6\x99\v 29\x9d(l\\\xd6\x1a\xbe~\xfd\x06\xb6vv\xbeOD\xdeMDO\u007f\xf9+\x8f\xf0\xea\x91m]\xcc\xd7\xebm\\\xb9[L\xa8o\x91n\x13\xdd\x00\x95\xa2\xe6{\x0f\x81WA\x87\xb6Ia8\x88\x8a\xafM%\xf6-\xd0\a\x89\x18Q\u0418fp4,\u0265:h\x00<\x14\xb4\xbe*$X\xc6Fv#\xcb+l!\x9b\x06|\xe4\x82JD*\xdb[\x9a{\xe0\x10\xc0n\xe9\x94\x18\x8a,\xe5\"Z\xee%\xe9\x8c\x10(\x10\xf92J\u0751\x97@6\u0556_\xa5\xf5oe*%\x80\x9cx\xd0\xccg\tyX\xbd\xa8\xb5\xac8\x0f:\xea\x01\x03\xb8M\x13\xca\aeXb\x18Y\x11\v\xb8\x94\x87\x88\xdce\nj\x1dV\x91\aR\xed\xa8\x12:\xd3\xf85bW\x1eA\x1d\xd7\v\u0731\x83\x9bi!G\x9f3Y%\xf8\xbe\xe4\x03\x00\xc1\xf4\x82f\xee\xe1C87y\xc5\u0717\x0e\xe8\xe7\x1e\xf3F\xb0i\x19\x1b\x96\xd4y\xb3\xb8\x8e\xd1q\u0449(\x1f\x1e\x02\x1f\xa5\xf6.\x18|1A\x1cU\xa7\b\xef\x83\xef\xcaR '\x0en\xe1\xd5\x0e\x80\xc2\rCq\x98\xeba\xed\x04[\xb7]\x00\xb3\x85x\x970\xf7\xc0\u28ae\xefq\xed\xda\xf5\xb3\u007f\xfe\xe7_9\a\xe0\xe9?\xfd\xd3?\xfb\x96rN\\\x17\xf3o\x83\xb5\xb5\xb5\x05\x00\xd8\xdd=\x83\xa6\xb1\x15\x94\x91Q\b\a\x17Zg\xf1Y\x99W\x9e\xe3%\f\v1\x92\xb5\u0245\x97\x85\xbeIb\x89I\xe1%=\x90\x0f\u055e +\x8aCY\x1dv\x9e\xd2\xcc{\xc9](H\xbbt\xf4\x12\xb3\x12\x10\xea\x06\x8c\x03\xe8\u062b\xf9\xd56\xe7\xe1\x1b\xe5\xee\xb9T|Fiz\xacR\x14v\xa7\x98Z_\xbd&\x1a\xf7\xa1\xc90\x05\xad\x82D\x9d\x00\xc7\x0e\xb2\x10\x90SJ^\xec(\x97\x02\xf4\xe1\x1d\xea\xf5\xf3=\x11x\xcahL\xceT\xa5\xe2\u0691T\a\xaej\x10-8\xd5Q\xb7\x86\xbe\x90F\x11\t\x1f\xf7\x92\xdf\aT\x9c5?v\xf0G\x0eXx\xf5l/TW\xb9\xb3\x0f\xc6`a\u00f0K\x81\x9f{,6\xb5;\xe7\u008fg\xd6\t:\xe7\xd0y\xc6f\xc3hM\x11\x10R^\xe0b\xa0\x1c\xd9A>X\xf8:\x0f\xf4\xbd\xa0\xef\xbd\xd2\x1d\xbdW\xe8m.\xa0.\x04\x0ez\xa9g\x0f\xa44\"\xdbN\xb0}\xf6\"\xda\xcdm\u030fn\f\u0384D]\xef\xd0\xf7\xee]\x8f|\xf9O\x1e\x04\xf0\xa5\x97_~e]\xcc\xd7\xeb\u05ae\xf3\xe7\xcf\x03\x00.^\xbc\xd0L\xa7\x1b\xa6\n\a\x0e\x0f]\xdf-\xe1l\x0f\xd9d\u040c\x13\u034b|\xf4\xba\x88\xe7\xe7,d)\x8b'\xb3\x0e\xfe\xa4\xc8\n\x95\xa0^\fv\x86`\x89r\xf2\xec\u0597\n\x10\x8d\x90\xd1O\xa9\xdc2\x98\xd9\xc5\xe3\xbf\x10@N\v\xa4\x84\x82.\xbd\x84\x87\x1a\x80\u04ce\u03c8\xc0\x1c(\u03dc6\xa3\xf2\xb2\xe8\x9c\aa\x10R\x85\x86Rm\x8aM\xd9\xe50y\x84\xa0\x8c\xb2\xab\xc5P\x05D\xaf\x1d\xe6Qx[\xc6\xe0\n\x86\x17\xe5];\x0e~[>lT\xc7\x0e\x1d\x01\x1e\x16\xdc0,\x11\x1a\x124&\xe4rV\xae\x88X\x11\x14\xc5\xea\xee\xb3\xcd\x14\xa8\xc4\u05fddc4\t\xb1|\x11s\x0f\x17\u018b`\xe9\x05\xb3\x85G\u007f\xe4`\x97\x02\xeb\xea\xefYN;d@Ae\xa7\xddy\xdf\x10\x1c\xa9\x1f\v\xdb\xfc\xba\x9d\a\x0e\x17\x1eK'\xd8j\x18\x9b\r%\xc36?82\xa9s\x80\xa0\xf3@\u702e\x03\xba\xa5G\xb7\xe8\xe1\xfa\x108\xed\x05f\xee@}T\ud19d\xaa\xa0\u062a\xca\x13\x90\xdeac\xeb,v\xce]\xc6\xec\xe0\x9a\n\x95(:4\x12\x11\x91;8<4_\xf9\u029f\xee\x01\xc0\x9b\u05ee\U0007a62f\xd7-]\x97/\xdf\x05\x00\xb8\xff\xfe\xf7\xbch\x8c\xb9\xd64\xed\x9d\xfd\xa2\xcb\xcf\x05\x11\xdcb\x8e\xc5\xec\x00n\n\xc8\x06CN\xfaB\xf4\xa2\xc5\\\xbcW\xe2o\xe9~H\xc8\x11k\x94'\x9a\x12\n\xba\x17\xc0\x05\u05fb&(\xf6R\\[=\u007f\xac\xbaX*\xf1p\xac\u01b6y\x19\xf4\xbf\xf1k\xb9\x80\x89\xfb\xe8\xa1\x12\xbbv\xfd;\uf05e\xb5\xa80<\x98\x18<\r\xa1\xc8Bu\xb0\x05Q\x96J\xa6\x02?\n\xe9Ws\b*\xe2\u0688\xea\xd7.\xe9\xe8\x0f\xc8\u0703\x0f\x1cp\x02\xf8>\x9ayi\x01\xf1\x84\",B\x92\xa8\xca\xce<\x16\x8d\xc7b\x9b\xd0A\xb0\x00`z\x825:\x18l\r\x05\u0719j%\xfe\x00W\x8f\xa9N\\0c\xbcD\a\u00c8YQ\xc2\xfe\xbd\bz'\x989\xc1\xac\xd7\xeez2w0\xbdO\x1d9\x95\x1e\xe0X\xfd\xfdD\x1c\x9a;\xa0\x9dy,\f\xa9\xf0H\xa8\"\u0590\x00\x8b^\xd09\x8f\xce\x01\x1bF\x1d\x19\xf1\xb2~\x8c\x00\x00 \x00IDAT\xbd\u03ff\xf7\xde\x01\xb3\xcea\xd1y\xb8^\xe05\xd5\x0e\xce\xeb\xc9\u0487\x19\x89]z\xd8pj\x90h#_\xdcx\n\a\xe9\x89\xcb-\xe6\x98l\x9e\xc1\xd9K\x0f\xe2\xb5\xe7\xfe\"5/\xf1\x04\xd6\u0616\xbf\xfe\xf5W\xf0\xe0\xbb\xef{XD\x1a\"Z~+\u0541u8\u0177\xc1z\xdf\xfb\x1e\n\xb38z\xcc;\xf7\xfc\xf6\xf6\xb6\xfa\xaf\x14\x86L\xbe_`v\xed58\xe9\xd0oY\xb8\t\x87pa\xed\xd0<\xbc\xfac@\xbd\xc3\xd3s\x1bR\\$\xfc9r\xc0L\u0228,:\xea!\xfb\rU\x06\xc0\xcaG\xd5\xe7\u0240\x12\x87S\x86~]\xe8r\x9d\xc0\xf7\x02\xd7+\x03$B\xdc\xec\xf5\xe3\xae\a\x163\xe0\xe4\x06p4\x03fN\xd0I\x0e7F\xe2<\x97\x85\x9aV\xe97$\xab\xaf\xbcP\xe6D\xf6\x8b\x17\x81s\x0e]\xef\xd1y\x8f\xbe\xf7\xf0\xc7\x02wBp\x1d\xc1{J\x9f\x1b_'\xc5\xea\x1a\xac\x03\xd8\x01\xdc\t\xda\x13\a\xd3\xe7!d\xe7\x05\xb3Np\xbc\xf48Zz\x9ct\x82E/\x8a/\x17\xb8\xb9H9\x89(\nZP\xcc\xc6k\x9c\xdf\x04\xcey,\x9d\xc7q'\xd8_x\x1cu\x82\xce\t\xcc\xc2\xc3\xf4\xa2F`R\xc8\xe7e\x04\f\v\x03W.\x14\xbev\xe9\xd1\u0303\xaf\x8a[\xa5\xd3pPv\x1eu\x0e7\xe6\x0e\a\v\x87\xe3\xa5\xc7I\xe7q\xb0\xd4?;Z\n\x16N}\u1f68\x8a\xd9Z\x03n4$\xda:\x82\xedC\x01\x17\x02\x87\x1f\x8cD\xe1\x1d\x16$=\x82x\a\xe9{X3\xc5\u079d\xf7c\xb2\xb9\v\xef\\:\xbfH\xd8\xd0Ofs\x1c\x9f\xcc>p\xfd\u06ab\x97\xbf\xd5\xea\xc0\xba\x98\u007f\x1b\xac\xe9t;\xbd\xff\xc0\xbb\x1f\x90\xdd\xdd\x1d\xb8\xbe\x97\x12\xb7p}\x8f\xe37^\x86_.\x81\xb6A\xbfi\xe0Z\n\x1d\"\x82\x90\xc3\xd5>\x18a\xe8T\xce2)\x14p\xa6p\xf4g\no\x99;\x1c\xe1\x199e\x92I\x83\x02\xee\a\x03\xd8!\xf7\xbdt\x00t\v\x8f~\xe9\u0447\xff\xfb\x98\xff(\x9a\u00e9\xff\x97\xf0 \x03\x98\t\xba\x03\xe0\xc6\xc2\xe0ZG8\xea\t\v\x1f%\xf4T\xc0%\xa5 ^*+\xf0\xe8\xab\x18i\x9a\xf1T\x12\xdd\b\x9d\xf3\xe8{\x87\xcei\x81u \xf4\vF7c\xb8\x9eV\x87\x8f\xe1\xd8\x1fO\ni\x18\x1d^\xbbY\n\x9a\x93\x9c\xd1Z\xb2F\x97N\x8b\xfa\xc1B\xdfN:\x85,\xa2\x16)\xbfI*R^j\xa6J,\xee\xcb^p\xb8\xf0\xb81\xf78X8,\\\xa0\xeby\x80;\x0f^\x86k\xeasW\x9eSz\xa4\x1e\x8bH\xa9\xc2\xd7\xe1g3\xf3\xb0s\u5967\xc3P\xf1\u03e3\x1dD\xe7\x04'K\x8f\xa3\xa5\xd3\xcdj\xa9\x1bX6\x0fCrP\x8c7\x1a;\xc0v!{\xd5\xd7\xe0\xbf\xf4\x1e\xe2\xf4M/\x8c$\x9bg\xdf-\xb1{\xee2\xce\xdcy?|\u07e5P\xedxk.\x97=\x98\u0307\u007f\xef3\xff\xfa\x01\x00x\xf4\u047f\xfa\x96\xc1\xcd\xd70\u02f7\u067a\xfb\ueedfi\u0513\x99\x04\xd9\xe0\xd9\xf7=n\xbc\xf8,\xdcb\x06\xbb\xb5\x8b\xaea\xf4\xdb\x04C\x0e4\xcfT1\a\a\x8e\x14<\x92\x14zL\x05K,\x9d\x95\ubf00\x02\xad\xa0\xf4\xac3j\xd6\u02f0\x1b\xc7\x00F\xa9\x1a\xdfb8\xd7;\xc1\xe2\xc4A\x0e: \xf0\x9e9\xc8\u096a\x109L\xc2\a\x8c\xdd\xce<z\u00d8o2\x16\f4\x0e\u0630\xc0\x86\x01\x1a\n~\x88\x15\xb6\xe2Wp\xe8\x01\x05^\x8b\xa4\x0f\xd8wP\x18\n\x18=\x18]G\xc0\xb1\xa0Y\xa8\xa4|\x84\x89\x9e\xe6\rTX\xf8\xa6k\xdd{4\u01ea\n\xed6x\xc5\xd4\xd0\t\xe0D\xb1\xe4\xb9\x13\x85_\x98\xd0\x18Bc\x82M1Q\xf6='d\xf6\x87\xd3\u03bbs\x82y/\xe8\x00\xb5h@a\u0535p\xb03\xddTHj\u06c3\xd1\xe94\xb279\x87_\x98'\xc0t\x82\xf6$\xb0[lpQ\x1c\x8a\x82\v\xae\xb8/\xf4\x11%\xd5R\xfd\x82(\xc1~\x1cpr\xd3\xf9\x9c\x13\x1d\x8a\u007fd\xd8\b\xa5@\xbc@\x99U>\xbf\xef\x97\xd8\u07bb\x80\xf3w\u007f\x00\xaf_\xf9j`\xf4P\xe2\x9e\v\x80W_\u007f\x03\u007f\xf5\xb5\xc7.\x02\xc0\xfb\xdf\xffAY\x17\xf3\xf5z\x87\xba\xf4\xe9\x1f:\xe7~\xa1i\xec\x8ew^}\u02a1\xc7\u0323W_\xc2rv\x8c\t\x1b\x104,\x19[\xd0\xe0\x85\xb9\x1e\x93\x1d{\x80\x03\v!\xa3\x00\x11W\xc9L\x16\u0285\x8e(\x17x)\xcdGiP\a\x06\x05{\u0629\x97\x03\xd0\xe4+\xee\xb5\xe8\x1c\xcd\x1c\xfa\xfd\x1e\x93\x13\x8f\xb6\x0f\x1d#ibO5[\x95\xb0\x01\xf9\u8ded\xf8\xf9\xf4\xa4\aL\x83~\x83\x94E\xb2\x04f\x860eB\ub056\x05\x86\x82*r\xe8\xeb\x8e2\x18\"\xc0\x13\xa1\xa0\xc6b\xe2\xc0\xe8`\xb0\x14\x03\xcc<\u06a5\x86YP\x1c\xbe\rmi\u00b55^\a\u007f\x1eY|K\x02P\a4'N\u00ec\x1b\xca\xf4\x93\xc1\xcb\x12\xd1n\xbds\x02\xe34\xf9\xa7\t'%\x13\x8d\xc8\u0086\xd8-=\xbaN\xf9\xdf.\xea\x97L\x10|E\xcfz/\xb0s\x0f\x13^\xff0R\xaf\u0718\xaa\x01w\xf8\x05s\xfcf\x81'n\x96\x1evF\xe8ZN\xa7<ZqX\xf6yn@R\x8b\x83H\xb7f\x82@X\xc0\x8e`\x96\x00\xbb\x1c\b\u0392\x95\xc0\x948\xec\x94N\x89y\b\x1f\xaf/\xe1\xcc\xf9wac\xfb,f\x87o\x82\x9biz-\xc6\x18\\}\xf1%|h\xfe\xc1\xef\xf8V\x13\r\xada\x96o\xb3\xf5\xc9O~\xf2\u037d\xbd\xbd\x05\xb3J\u0563k! X\x1c^\xc7\xc9\xf57\xb2\xc0E\x00g\x19\u0766\x81\x9b\x10\x9c\x01<\xc5\xd2\x12n\x10\x8fp\x94\x95\x14\xdb\x06_\xfb\x84\xa4\x87RV\xc5\xf3\x83:[\x9b\xf6\xa1\u019d3.\xadP\xc0\xb2W(\xe1\xc6\xc2\xe1d\u1065\x87q\xb2\xf2}V\x1bFI\xd49\n\xbc\u0148E\xdb.s\xab;'8X\b\xae\xcf\x04\xd7g\xc0\u0452\xb0\x14N6[\x92\u07b40y\xaf\xc7\xffX<E\x00'\x84%\fN\xa4\xc1\x89X\xb8\x0e\xda\u057a\x9a\xcaW\r'\x91\v\xa7\xe9\x14\x9b6N\xaa \x10\x88\xc0,\x14n!\x87\xcc\xef/\a\x8e\xc5\xff\xa1\x89uX8\xc1q\xe7q\xb8p8Xx\x1c.\"$\x13 \x8c\x85\x87\"p\x83IF`\xf5p'\xb0s\a\xee\xe54\r\xd7Hg\x9eOF\xc9Z\xc1k\a\xcd^\xd9-f\xe6\x14;/\u0607\xb9\x1f\xf0\xc9?eh%\x11\xe3\xf0b\u03a7Y\nl\xef\xf3\xcf\x1e\\(\xa3ChY|%\x85r\xf8j\aq\xcb9vo\xbf\a\u06f7]B\xdf-\xc2\xd0\u06a7(\u0093\x93\x19\xf6\xf7\x0f?\x02\xe0N\x00\xf8\xf2W\x1eY\x17\xf3\xf5\xba\xf5\xeb\xfd\xef\xff\xe0\x17\u039d;\xf7\xc6\xce\xf6\x0e\x9c\xebU\xea\xac\xe7N,g\xc78z\xf5j\x90?\x1bm\xb6Az\x9c\u07f6\xe8'\xac]\xa2\xb8\xf4`\xc6\xeeRz\xed\x92{\xe5\xbd\x148l\xae\xc2%t24&\x1c\x1bl\n\r\xc2*\x82\xc4z\u953ev}\xeeq\xb8\xf4\xe8|P\xe9{$\fw\x05Q\x1f\xa8;S7\x16pt\b`\x16\x1e\u0371\xd2\xd8\xe2 \x17\xd0\xc2|\xb4\x10\\?\x11\\\x9f\x11\x0e\x97\x8c\x85#\xb8\x98?\xeaU\xe0\xd3y\t\xb8\xb8\x1e\xfb;0N`q\xec-\x96PFh3\xf30]\xc0\x88]\x06\x94ip@I#WO\xa1\xf0\xe9\t\x82b\a\x1c\nU\u011d\x87\x85|Xai\xf0u%\xc01K'\xe8\xbd$H9\xf9}Sa$\x16\u007fg\"03\x9f\u1555\xcd2\xb3\xf3\x05\xa7\x15z\xc9\x03^\xe8\xef\xca\xf6\x1e\xed\u0301;\xc9\x12\xfd\xf0\x83\x88s\x90\xde%\u06a4\x1f1<\x8b\u0775Y\uad48\xa9D\x14\u007f\xb7T\x06\x87\x87\xfb\xc8\xc7B\x1emh\xf2=\xe2\x9d\xc3\xc6\xf6Y\xec\xdeq\xb7f\xb9\xa6\xcdDW\xef\x1c\x0e\x0e\x0e?\xfa[\xbf\xf5\x9bg\x157\u007f\x9c\xd6\xc5|\xbdn\xd9z\xf2\xc9\xc7\xc2\xf3A\xb3\x87\x1ezh\xbe\xbd\xb3\xad\x0et1a\x86\x80nq\x827\x9f\u007f\x02\xde\xf5\x1ay\x96\xfcE\b\xd22\xba-\x037\t\x93\u007f\u7a87\x99\x9c@\x16\x1e\xae\x0f\x98-\xb2\xce9u\xe8\xc9f\x16\u0574k\x14B\x89\xb2\xf1\xf0q/\xda\xf1\x1eu\x1e\xd7\xe7\x0e\xfbK\x8f\x85\x93\u031c\xf1\xa2\xec\x8a\u00a8\x8ad\xd0\xfb\v\nA)\x15Gp\xfd\xbb\xd8%\xdaY\x96\x84S\x81 9/\x98-=\x0e\x16\xc0\x8d%\xe3\xa0c\x1c\xf7\x8aK/\x1c\xd0{RL\x1c\x063\xb18\xf6\r\x16\xde\x04a\x8b('{\xe1\x146\x90|\xe4\x90\xc1IE\x068\t\xf9\xf0Vp4\xd3|\xd1iwn\x96z\xc2\x12)q\xe6\u054d\x92\x06oLyp\x9dy\xf2\x94<\xc5\x11\xf4\x02B\x00/=\x9a\x99\x039\x19d\xac\xd6g\xad\xfc_\x19?\x19\x85\x91\ny\x81\xf1\xca\u04b1s\x81=v*\u02e7|2\xf4\xce\xe9\xfd&\x1eU<w\xa8\xec\xe9:t\x1ev\xe6\xd4`-\xc4\x05r\xf8\xbf!\xceXy\x11\x8f\x17\x1d-\x93\x127u\xec\xfaL\xec]x\x00\xd3\xed\xb3\x10\xd7W?\x87\xf7\x82\x17_zi\xf2\xda\x1bo\xbc\x17\x00\xfe\xb3\x9f\xffyY\x17\xf3\xf5\xbae\xeb\xc1\a\u07d7\xde\xdf\xde\xde\xfa\xacZ\xb4\x1a=>\x86\xc1\xa0[.p\xfd\xf9'\xd0\u03cea\x8c\xa9\ak\x02HCXn\x19\xf4\x8d\xde\xf0\xa9\x93\x11\x1d\xf6a\xee\x80\xc3\x1e\xbe\x93@ P\xec\u060b\xfa\\y_\x02/\xf5\xf0-\xf1@\x12\xcb-\xc0\x14\x01\xb68\\\n\xae\xcf=\xf6\xe7^y\xceR\xe6F\xaa\xf5*\xf7yhXU3\xd4\xd9qe\x1c^\xb4\"H\x96\xb1\xbd\xa0\x9dy\xd8P\x1c\xd3\xeb\x93\x1c\xf2 \"Xz\xe0\xc43\x8e\xbcv\xde3o0\x13\x83\x13X\x9c\x88\xc5B\f\xfa\x98\xfaN\x00\xf5\x80\x9d9p\xa0\xe1\xb1S\x88a$ \xaf\n4\xa3\x04I \xb1q\xaa\xd0\v\x00\xa6\xf3\xb0\x01n)\x9fX*\xf1w\u0526\u00e8\x1b\xd5\x02\u007f\x1e\xf9;\xaf\x83\xe2&\f=\xd9\xcb*l5J0\xad7\xd2*z/\x15\xf4\xf0\xf3\x85M\x89\xe7>o*\xde\x01\xbe\x0f\xf7\x1a\x06\xe1\x1b\xf9\x9e\xa1\b\xd5t\x92\xba}v\x8a\x93s\xc4\xca#\x93%\x06G\xa7$\xa2\xb0\x891U7\xa3w\x1d\xce]x\x0f6vnG\xdf-\xf2s\x02\xed\xe2_~\xf95\xbc\xfe\xfa\x9b\xffa|-\x8f>\xfaW\xebb\xbe^\xb7~=\xfc\xf0w>\xb2\xb3\xb3\xdd5\x8dzP\x88wZ<\xbd\xc7\xc1+W\xb0\u007f\xf5\xa9\x9c\xb2\x1e\x8bvx\x13K\xe8\xa7\f\xc7\x01;\x17U.\xf6^\xd09\xa0?\xec\xd1\xdd\xe80_(7\xf9h\xeep8s8\x98\xf58\x98{\x9c,\x04\xb3\xdec\xde\v\x96}\x80%\x9c\xaa4]`\u007f\xf4^\xa1\x94y'8Xx\xbc9s\u061f;\xccc\x11G\x9d\x0fMPf\x04\xfb1,gP.\x05+JL\xf2\xc1\x9b;*\x06\x97\n\xb7p\xef\x87_\xa4*\x86\x19\x13g,`\xb0\x80A/\x9c\xb4\xb2E\x9e\x05\x9a\xb9S\b\xc0\xe5b\x13\x8f.C\xf8\xa3*\x95\x9c\x95\xba\xe4\x03-\xb0\x1c.\x04\xaby;\xf7h\xe6\xaeH\x10\x02\x06\xc9\u007f(\xffKE7N\xa5\x9d/e+\x83\xf2E\u0645v\xe5\n\xf5\x10\x86{\xf2*\u06fe\x18SS\u044dK6k\u031b\x8dRE\xcd\u00a39r\xaa\u0685\x069\x8b\xf39\x1c\xa3\f\x9e.6\xaaf\xe1`\x17>\f\x8cI-\x1bb\x17\x1d\xe9N\xd5\xc9 \x8cU}Q\xd0\xc3&\xedS5\x17l\x9d9\x8f3w\xdc\x03b\x0e\xf3\xa5\xc0.\x12\xc1\xa2\xebppp\xf0\x9f\x1f\x1d\xbcy)\xc0\x97\xebb\xbe^\xb7~}\xfa\xd3\u007f\xf3_\xbd\xfb\xdd\xef\uedb7\xb6 \xe2\xc5\xfb\xc8Y&\xcc\xf7\xaf\xe3\u0367\xfe2t\ub738\xd2U\xe0/\x03\xdex\x1d\x86\x86\xa1gT\b.\x17\x1e\xcb\xeb\x1d\xe6\xd7:\x1c\xcd\x1c\xf6\xe7\x1e\xfb3\x87\x1b3\x87\xfd\x93\x1e\u05ce{\\\x9b9\\\x9f\xe9\xe0\xf2\xc6\xc2c?\f\xe1\x0e\x97\xf5\xdb~\xf8\xb3\xa5\x93\"\x86\xae\x1e\x98\"tb*`\x89\xbe\x1b8\u0144\xe4\x94a\x9dW\x00\x99|\xaeLv\xee\u041c\xf8\"<a\u012e\x97\xea\xa24\x9c\a\xc4a\xa5Y*\xd6\x1c\x8b\n\x87\x82N#\x18D\xad1\x1d|\x14:d\xf8\"\xb0)\xc2\xee\x0e\xb0'\x1e\x1c\xad\f\x06\xa7\x90\x95\x92\x9b\xe6\x19y\xa0(E\x98\xb1 \x17M\xee\x05\xf6D\x19,\xec\xe4T\x81\x00U\x00}\x9d\x105\xac\xef\xd5\xec\x02\xb9C7'\x0e<s)d\xda\a\x88%u\u5147<\xc5k{\xe2\u00c6L\xba\u0641T\xd9K\x9c\x99Ta\xf8\xc9E89\x0f\x9d@\xa9T\x88\xea/\xf0\xf6\xcb\x0fa\xbay\x06\xe2\xfbt\n%f\xf4]\x8fG\x1f}|\xeb\x9f\xff\xce\xef>\xfc\xad\xf2\u072f\x8b\xf9\xb7\xd1\xfa\xdc\xe7\xfeM|\xf7\xf0\x81\a\x1ex,\x18\xec\x93\x1e=\x1d@\x8c~~\x8ck\xcf?\x0e\xd7-S\u01e2\x1d\xa0\xd3\xf3\xb6\x0f\x82\v\x118xx\x1e\x1c\xa8\xc3d\u035c8\x98\x13\x95\xd5\xfb\x88\u007fz\x95a\xf7=\xb0\U001024e0Z<(D.\x87\v\x8f\x93\xa5`\xdey5\x99Bf<\x8e\xc0\xb3\t\x1aQ\x88%;\xf5\r\xd9\x18\xc3\x0e\xbb\xa2\u03608\xf2\x17\x1bDs\xa2\xdd4R\x105a5e3\xe3\xd442\xc5$\xafE\xd6tR\r.\xc9I\x8d\x8d\xaf\xbc\xea1P\x04`\xef\xf3\t\u0123\xb0\x8b\x11e\x9a\u031c\xfa\xe9\f.\x97/f\x18\xe3_y\x14\x91\x82\b`\x16N!\x90N\xf2fYz,\xa4\r`0\x9f(\xbd\x04\u02bc\xcd\xc4%\xaf\xbfQ\xb4\xca5'\x0eX\xf4\xa1y\xf0\xa9\xa1\xc8\n\xd6p\xedz\x0f;\xeb\x95YS\x0e\xbf\u3f03\xa0\x05\x9d9\xf1\xc4KK\xa2tm\x12S\x86R\x8c\\\x9c%\x9d\xbb\xf8^L\xb7\xf7\u0f7e\x9eHGt\xce\xe3\xf5\xd7\xdf\u011f\xfd\xf9_\xfcd\xfc:\u007f\xf0\x87\x9f_\x17\xf3\xf5\xba5\xeb\xe3\x1f\xff\xe1\u0521L\xa7\x93\xff}cc\n\xdb4\x19\x13\x06\xe0]\x8f\xa3W_\xc4\xec\xcd\xd7@\xe0\u0288\x88\xc4\xeb0\xc8\xf5\xc1|\xcb\xc3G\xbb\\\x1e\x84\xe0:\xf5\u07d8\u0303\xcf4\xe5#u\nA\xa6\xfa-w\x87\x92\xa9u\xc3r\xb7\x12@\xa4t9v\x12X,\x85\xb0u\xd8\x05\x16\xb62\u547f\xac\xca$\xb9`q\x1f\x86\x8b\x9dO\xf4MB\xa6>&D\xa7t(\x1c\x90<x\xe9\xc1\x91S\x1ep\x17\xf2\xc3fYN\xd9t\n\xbb\xe2\x02xfW\x84\x82\x94?c\xe4\x80/|\xbd\xd9\r\xdeV6\xa3\xd3pn\xa2\x84e\x9b\xa5W\n\uaa7b\xc0\x88)\u02d8A\v\x06\x9e\xf6\xc3Y\x86\bx\u0583\x8f\x97jX\xe3\xa5\xe6\xa6\"_|;\xd7\xd9F\xb2\n\xf0\xf5\x04=\xbf+\x03S\xb6\xda\xf63R\x16c2T\x9a\xb9x\x87\u035d\xdbq\xdb\xf9\xfbA\xa4\xac\x96b\xfe*'\xb39\x9e\xbfr\xe5'\xfb~\xf6>\x00\xf8\xc1\x1f\xf8\x18~\xff\xf7?\xb3.\xe6\xebuk\u05f9s\xb7\xff\xf1\xddw_F\xdb4U\xcc\x16\xb1\xc1\xec\xfa\x1b\xd8\u007f\xe9Y\x95H\u01ee(\x98ei\u01e2]\xbab\x8c^9\xce<pA\x14e*\xb4s\x8fvQ\xc3\x15\xe2W\x9fu*\xe0\xe1*\xc3\x19\x18\t!\xce\x0f&y\x81\xe9}`X\xc8\xc8\xe0M2\x1c1L\xd5I\u07ae\x19;\xa7\x00x\xc7\xee\x91{\x8ff\xa6Xw\x15\xcb&\xa8L\x14\u01ecz\xc9\x03\xcdB\xd2\xd03\x16\u0728F,q\x109-\"o\xe4''\x0fp\x1f\xe1\x1a)\x95\xf3Z|g\x1e\xa6\xaf;\xechx,2\u019a\xa9\xf7\x8f2T\xa3Yz4s\t\xbcr\x19\x84\x84\f\u01b6oa\xaf\xbb\xe2r\x9c\\\x12\xa8\xb6F\xee:-\xe6K\x97\xe62)\x05%|W\xc5\xf0%\xcd;8x\xac\fO\x00\xf1\xd4\"\x85\x1d/E\xf8%n'Q=*R\u51ca\xf7\xb0\xb6\xc5\xc5\xfb\xbe\v\xcdd#\xe5\x8c\n\x00f\xa6\xa3\xe3\x13<\xf5\xd43g~\xe37\xfe\x97\xbf\x15\xbf\u0697\xbe\xf4\xa5u1_\xaf[\xb3\x9e|\xf2q\x00\xc0\xe5\u02d7\xaf\xdeq\xc7\x1d/\xd8@A\x8c\x8f%\x1b\x83\xf9\xc1\x9b8\xb8\xfa\x14L\xc8pL\xc3\xcfp\xa3\xfbP\xd0)\xc0.>(>c\xaaL\x1c\xf2A\xa0\x05}\xe6\xd1,\xfch\x81\x1a\x0e\xff\x860\x80\xc88\x04\x90:{\xa7\xf0\x822=h\xe0'^\x1f\xab\x85\xea\xae0\x16\xfe\u0215O\xf3\xba\x82sL.\f\x17\x17\xbeN\xfe\x19r\x97\x87\xb5+\xe2\xb9K\x9f\xd9\x1f\x82\x84\xeb\x0f\x85Q\xd5\xd7\xe128{\xac\xf4\xea\x06\xc1\x9d\xcf\xd0G\xb1\x91f\xbeuY\u0425F>\n\xf8E\x06\u05d7\n\xf8J7\x86|Z\xa1*\xf7\x94\xea\xd3C\x89\xa6\xac\b\b\xa4\xee\u02a5t\x12\x0e\xb4J\x0eZO\xe7\x82e\x80\x0e@3\"\x13\xe4\xfa\x9d\xc0\xc4al\u0090d\xe5^-\x19WD\xa5NW*x'\xb2[\x12\x9b\xcaG\u058a\xee\xe0g/\xbe\a;{\x17\xe1\xbd\xcb\x1b\x1a\x11\x8c\xb1x\xf3\xcdk\xf8\xccg>\xfb)\x11\xd9\x01\x80\x8f}\xec\xe3\xebb\xbe^\xb7f=\xf8\xe0C\x00\x80\x9f\xfa\xa9\x9f>\xd8\xdb\xdb\xfbB\u06f6`\u00c1k\xabC\xcf\xe5\xc91n\\}\x1a\u02d3\xa3\x18\xe1[\r\xb1H\x04pN\x99\x06\xe2A\x81s.&c\xc2%\x83\x80{\xc1$\x14\xf4lBU?\xectJSJ\xa7\xb4\x91\x11\xdePJ\xe2\x00.\x19<\xb0\x82\xa2\xed_\xc9o\x96\u02ba5}\xdf\xc2@\x8a\x05hO<\xec\u04af4\xa1c\xf0/8\f\xf3\xe6n\u0415\a\x16\x8b\x8c`\xf9E\x9b\x1f\v\u06eaD\xb6\xa8\x86\xc1k\x9e\x9d\xa8|\xdd\xe7\x93\a\xf9P\xec:\x19\ud307\x8c\x9c\xea4Q|\xbe]ze\x89\xf8b\x17\x93\x9b\x1f \xe8\xb4\x1dz\xe0\x9f^}\xef\xb8\x11\x10\xb41\b\xc3\x15\x9e;U\xbe\x96\xa6\x95\xc1K\xc7v\x92\xbfF0\u02eaY/>\xe1\xec\xbe4v\v\xc5\xdb\xc7\x01k\xf8\xfb\xf8\n\x15\xf2\xe3tO8\xd7a\xb2q\x06\xe7\xef\xf9\x0e\xdd\xfc\xbcK/\x9c\x8d\xc1\xc1\xe11^}\xed\xb5\xef\xfbG\xff\xe8\u007f\xfay\x00\xf8\xc1\x1f\xfc\u063a\x98\xaf\u05ed]Dtt\xd7]w}\xfe\xe2\xc5\v`f0s1\xe8\x13\x1c\xbc\xf4,\xf6_z\x16\xc6\xd8,n\xf1>)\xe7\xbc\xf8<\x14r\x1e\xe4\x94\xc6\xe7M\xb0\xc1\x1d\u0d26WS\xa5f.\xc1r\x14+\x18\xab\xa1\x12\xd7\x1cU\xe1\xd7E \xc8\xf0\xb9\xf7\xa7\x88XV\xc1\xe1\xb2\x1e\x92\x14\x03O\xc9\xe3M\n\x02\"vY\xb0c\x9c`\x12\x06\x99+\xf3\xbf\x016-\b\xdd\xf1R\x8a\x84\xc8\xe0\a\xe3d\xe5\x1fJ\xb1C\b#\xa5<\xd5s\x88\x81\xca*b\xfeN\u007f~^\xe6\x0e\x9dD\xa9\x9a6\xc0C\xa3\x90\aF\xfc\xb1\n\t)\a\xbe\xbdv\xe5\xa7\u007f\x8dr\x97X1\x1b\x1b\xd1l\r\u060ai\x86\x13?\xe2 J\x10\xa7\xb3\x06\xbb\x90L\xf1\x14\x9d\t\u0639\xb2W\xf4z\x06\x8f\x98\xd8]\x87\xc1e\xa4\x12\"D\xcb\xd5\xd9\xdbTZx\xe6{\xae\xb8\xc1\xa2\xea\xd3\xf5\x1d\x8cmq\xe9\x81\x0fcs\xe7|\x8a\xac\x8b\u0670ll\xf7\xd2K/\xe3\v_\xf8\xd2/\x8a\u021d\x00p\xe5\u02b3\xb4.\xe6\xebuK\u05af\xfc\xca\u007fE\x00p\xe9\xd2\u017f\xb8\xef\xbew)\x9c\x12&\xfeD\x00\x1b\x8b\xa3\xd7^\xc4\xfe\x95'\xc0\xd6Vj\xf8H\x0f\x13x\x888\x888\xc0\xb9\x14\xa8\x9b\n:\x90\x94\x82\x1c\xe0\x05\xd3\t&'\x0ev\xa6V\xba\x15d1H\xa5\xf7#X\xb9\f\xba9v\x81\xc5\xe2O\xa3\xcb\xe5b(R\b\x8aV*L\xd1*\xa6D\x8b\x88o\xe7\x0e\xddvZ\xe0J\xd1\f\r\u00c4(C3\xe4jy+W\xcaI\xaa\x8bz\xd9\xd9S\xd1\b\x13%\u07d5\x15nf\x81\xfbG\x0f\x97\xf8\u0112\x00v\xe1t\x18:\xac_\x18H\xff\xa9 \xf6\x84\x8d\xc4v\x82&Z\xc8V\xf0\x04\x9d\x82w\xadZ\x13\x97\x11v\xe9\xe4B\x83kV~\xc5\xe81\xe0\xf3\xa0\xd8,\u00bc\x82\tv\xa9\xe2 .6\xad\xfc&\x89:\x1b\xa3\xb2\xa9\xb0\u01e5p\x8f\xb31\xfa1(\xdd\xf3\\\xec\xf4\xe2k\x1e;\xe0\xe1\\\x87\x9dsw\xe3\xc2\xfd\x0f\u00c7\xe1\u007f<\x1a6\xed\xc4^\xbb\xbe\x8f\x17^\xb8\xfa\xf0o\xfd\xe6?\xfeY\x00\xf8\xd1\x1f\xfd\xeb$\"\xebb\xbe^o\xff\xfa\xb9\x9f\xfb9\x01\x80_\xfa\xa5_|\xf5\xce;\xef|\xacm\xda\xec\xdd\x1dD\x14\x8b\xc3\x1b\xb8\xf1\xc2Sp\xf3\xe3\f\xc1 \xe3\xe6)\xbd\u073b\u0421\xbb\xc0\xb4\x90\xba\xdb\xf5\n\x85\x18'\xb0\xbd\xa0YxL\x0e\x9dJ\xe6\xe3\xb16\u05e4\xf1\x8e\xfa\x94\x8e\x90]\xe0\x97\xdf\xec\xe8_\xbd\x16I^\x1d2\u0491&\xaf\x16\xd4dA*\xbeV\xbb\xd0P\x05\x19qy\x8c\u03efYFW\xc1\xac\xd8DE\x9b\x1b\xd4\xf3\"T5\x16:\x1e\xebn\xe9\xf4\x83\a\x05\xf6\x8dv\xe2\xc1\xaa\xa0W/\x18ve\x98\xf7MN<\xc5&\xd9,|R\u050e\xa4\x88\xdc\xf4ZW\xaf\xad \xa3\xa4\x82=\x8c\x8d\xca^\x0f\xd9K\x05ZX\xa9Sv\x8e]\n\x9a\xc0\xa3\x1fK,\x11`esY\xb9FD\xf5\xe6\x01\xaa\xff,J\xfb\xe3\xe6D:\x87\xe9\xbb\x05\xect\x13\x17\xde\xf5\x1f\xa0\x9dn%\u07bb\x04\xdeU\xd3N\xdd\xd3O?\x83/\u007f\xe5+\u007f[D.>\xfa\xe8c\xfes\x9f\xfb\xb7f]\xcc\xd7\xebm_\x1f\xfa\xd0w)&j'\xcf\x186\xff\xfc\xfc\x9d\xe7\x035\x91**\xd7\xf5\xe7\x1e\xc3\xd1+/\xc04\xadv\xe0\xa5\xf1G\x14S{e\x1c\b<\xc8{eYt\xf1\x18\x1ce\xeb\xc1;\xc5i\x81\xb1\v\x8f\xc9A\x8f&J\xb7\x8b\a\xcf\xd7\xf4o\fO\xc7\xe5\u0411:\r:&\x8f\x82_>\xa80\x05$\x91\xeb\xd6\bH?\x14\xfc\f\xb1\xf3\xa285sI\xf8\xb9\x14\xafKB!\xb4\xa1\x9b,a\x1d\xf6\xa5(iX\x1d\xa9\xda`(%\xe2\x14\xf0\x0f\x8d\xb4\xd3\xe5F\x83<\f.C\xb2\xb9\xd3\xd7s\x1a#h\xac\xb8\x9b\x0e\x85@\xe8\x94O:\xfd\x0fW\x85U\x18\u02cf\x1a\x9c\xc8(\xc7\xe3\xb1\xcfg\x01\b\xd0\xcc=\xda\xc3\xc0s\x97b\xd0.\x999U~C\xa9\xe1\xc4B\xec\x15\xa1\xc2\xc8]\x8f\x12\xfd \"\x8a'T\xa6,\xff\x0f\xafD|\x8f3\xe7\xef\xc5\xd9K\x0f\xc2\xfb\x0e\xd9\x17\x18h\xda\xd6\\\xbb~\x03O<\xf1\xe4w\xfd\x93\u007f\xf2\xbf\xfe4\x00|\xe2\x13?,\xebb\xbe^\xb7d\xfd\xea\xaf\xfe\xb7-\x00\\\xbct\xf1s\x97.]\xd2b\xce\xf9\xcc\xcd\xc6`\xff\xc5gp\xe3\xca\x13`c\xab\x87(S\f3\xd4\"\u0781\x9c\x16\x00vJ\xe9\xe3h\x0eUL\xdd(\u061e\u0699C\xbb\xdf\x05N\xf4\xc0s|\x80qc\x88\x84\x14\xf6\xb0\xb4\xc2C\xce_\x84N\x03\x89cM-\xe3L\xab\xe8\xa2\"^,\xbc\xde\xe4\xa5\x12\xbeo{\x1c\u0516\xa8\x94\xf0\u02b4XH\xe5\xd8WBN\xe3\xfc\xbcS^\xa3d:\xa0\x10\xad\x0e\x15\x8bo\x1e1e\xd3\xf9`C\x90\xa9\x8a6\rC\x05r\x93\x1a\f\xd2Aj\xb3Tj#\xc9i\a#y\v\x10\xfe\x94\xfa\x9e\xb8\xfa%\xf5\x05\x95\x8fx,\xd2\x19W'\x95\xf9\u03dc\xdeO4vB\xc9>\xe8\xc4\x14f@Y\xe1)\xc3\x13\xc9\u0419\x11\x12\xfc\x85\xa2w\x8b\xcfP\x1f\t\xd80\x9c\xeb\xb0y\xe6<\xce\xdd\xf5\xbej`\"\x04\x80\x19M;\x91\xc7\x1f\u007f\x02\x9f\xff\xfc\xe7?%\"[\x00\xfco\xff\xf6\xffA\xebb\xbe^o\xfbz\xf7\xbb\x1fp\x00\xf0\xf7\xfe\xde\u007f\xf3\xdc\xed\xb7\x9f\xfb\xea\xe6\xe6\x14q,\x19\x8d\xb7\xba\x93C\xbc\xf9\xd4_\xa2;\xba\x0e\xd3\xd8|T%$\xc3\"}\x8b\xb8\xb9\xcb<j\x8f\x94\x0f\x19\xbdT*\x8c\xd3++\xa1=\xec\x83(\x87\u01a1\x84A\x8dM\xf4\xe4^]\x12\xcb\xd82:\xad8W6\xae\xa7\x15\x1fZ\xc1{\xd3\xd7\r\x83\xcbx\u02a0\xe4\xdf\xe2\x03\x8b$p\x8f\x03V\xae\x94\xb9\\\xa9J\x19~]~j*\xcf\n\xa4!c\u00c2\x12`\xc6`\xf8JI\xec\xa4\xddm\xf8\xb8\xd3\xc1\xa1HM\r\x94\x91Q\x83B\x1a\xc5F\fy\x8b\xe6{`t\x83\xdaO^\x863\x89\x91\xe6>\x0eX)am\xd10Ka\x16\xea}\xba\xa7J\xe8>\x9d\x98R\xfa7W \x8a\x14\x9b\\:qb\x10[\x18\xed\f\xbc\x87w^\a\x9f(\xf3R)\xcd\f\xc8Z\x9c9\u007f\x0f\xa6\xdbg\x83\xcd@\xaa\xf80\xb6\xa5\xfd\xfd\x03<\xfb\xecs\x9f\xfe\xb5_\xfb\x1f\x1f\x06\x80\x9f\xf9\x99\x9f\x95u1_\xaf\xb7}\xfd\xc2/\xfc\xa2\xbb\xed\xb6\xdb\xccd\xb2\xf9\xc4m\xb7\xdd\xf6\xff\xdd}\xf9.\xf4}/\u027c?@\x02\xaf=\xf6e\x1c\xbe\xf4,l3\t\xceq>\x1dU\xe3\xfb\xd2;\xf8\xbeW[\\\xef\x8b\xe3o-\x91/\xebP,\x90f.h\x0e\x1dh\u9aeeQd\xbcq\x8d\x0f\xbd\xe9$u\xbb4Zf\x8ax\f\xa1\x9a7\"7\xb9\xbb\xa5\xc6\xc8++\\\xaf\x05\x9b}\x10\x13\x85anb\xb0,\x94}\x81\"H\x19\xa1;&?\x024\xc8\xc8&\x82z H2\b`\xae\x03W\a\xf0svd\xa4\xbe\xe0\xca{\xa8\x0f\xf9|\xd5\xd9+A\xc4\x1c<X\x82\xfb\xe0\u0403e\xf8\x9e\x8c\xee\x9bTAF\x84\xa1\x9fM.\x8c\xa3\x9e4)\u03142S\u01c5T\xab\x18\x80R$\x03Qq\x02+1p\xfd\xbfY1\xd3)\x83)\xca\xc6$\xaa>\xa9\xf8s\x1a\x9c\x16\xc5\x10z\xe9\xb1{\xe7\xdd\xd8;\u007f\xafr\xe1#t\x13\u00a4A\x8cg\x9e}\x16\xaf\xbc\xf2\xea/\x8b\xc8\xc6\x1afY\xaf[\xb2\x88\b\xbf\xfe\xeb\xbfF\x00\xf0S?\xf5\x93\u007fy\xf1\u2965xob\"\x9a@\r\x85N^\xff:\xae_y\x02\xd2/a-g\xe7\xba8\f\r\xc5\\\\\x0f\xdfw\u06a5\x8fv\xd6T\b\x8f\x8a\x01\x97\x17\x98\xb9\xba\u5c53\x95;N\x06tD\t\x1d\x99\x06<\f9\x8c\x94\xc3\x1bP\x93\xffd\xb5\x8dEYOO\xeb\u042b\xfa\x19\x1c\v9t\xda\xe4\x94niB\xb0\xb1Y\x043\xad\xd8\xfe\x06j!\xb9\x01\xbd\xaf\x1c\x02\f`\xa5\xea\xba\xd1\xc8\xfb\xe5\xbf\x1b\x1c[\xa4\xd8\x00L\x1f\x95\xa7\x92\xb1\xf3Y\x98e\f\xf6\x85\xb8!\xc488._\xaf\xe4L%\f\x83\xa7Qdq\xa6\x8f\a\x89Ho\xb9Jf\x8aT\xc1\xcf)t[\n3.h\xf0\a\xa1\xf0\x81\b\xefk\x016 6A\xe5i*A\x9c\xe6\xd4r\x1a\xf6S\xe1\u0260\x11\x82yc(\xad\x99=A\x83\xa2\x8d\u00d9\xf3wb\xef\xfc]yK\x8a\xd0\f1\x88\rn\\\xbf\x81\xabW\xaf\xfe\xf0\xff\xfd\u007f\xfd\x9f\xbb\x00\xf0\xc5/\xfe\x11\xad\x8b\xf9z\xbd\xed\xeb\xe3\x1f\xff\x98\x03\x80\x1f\xf9\x91\x1f\xfb\xfc\xe5\xcbw}\xf5\xfc\xf9\xdb\xd1w\u02fe\xa4Uy\xd7\xe3\xfa\xb3\x8f\xa2?\xdeGc\x1b\x18\x04\xaaWY\f\xc5C|_\x99\x11U\x02\xf5\xd8\xed\xc6G\xa5\xe8\xdac\x92\x85\x9diA''\x95\xd7I*(\xa5wy\x18\xf4\xd1P\xc82R@\u0190\x02\x19\x83~G\xf3\u03a4\x1a\x8cR\xb4\x9c\xf5\x99\u007f\x1e\xfd[\x9a\x90\xc0\x13\xa7\xadTl<\xa3\x85\xfc\x1b\x01\x9aO\x83(d\x84:B\xb5\x1b\x19I\x18\x86\x16p\x8bYJ\xa2*\u018d11X:\x81\r\xb2\xfd\xa4\xac\x8c[aAK\xadh\x84+\u01bac\x9b#N\t|\xaeOP+\xe8Q\x18jK\xf5\xb3\x87\u035b\t\x9eB\x91M]=\x03\xc4\xca\xfd\xe6P\xd0\xd9d?\x18\xc4 \xf1\u0708\x94\xe6]\x12/\br\xb7\x1d\x87\xd7>\xd0:M#\x98\x9e\xd9\xc6\u0785{a'\x1bJS,n\"6\x16\xce{\xbc\xf0\xc2\vw\xbd\xf0\xc2\xd5\xef\x02\x80\xef\xfd\xde\xef\x97u1_\xaf\xb7}\xbd\xe7=\x0f\xc9#\x8f\xfc1\x11\xd1\xebw\xdcq\xfb\xffs\xee\xecYx\xef\vb\xb9>\xba7\xae<\x89\u064d7u\x10JE@3\xe7\aL1G\xa5(\xd2\xe0!\x1d\x01Z\x93qU\xc4H\xe1\xb5sl\x8e\xbd\x8a\x8a\x06Tp):6re\xfaN\x81O\xd3\xd8\xd9\xff4\xcd=U\x8a\xc8\xf4\xc5eDj*\x03)}`\xe7D\xb7\xc6f\xe6\xd0\x1e\xf5)\r\x88J\u067f\x94\xd4I\x19\x96\xbd\u0453\xc0J!<EQ\x996G*`\x87\xf8y^\x031RwN\x12R\x89\x94r\x88\xc0\xaf&\u039ctv\x01+\xf7\xab\xbf4\x19\xa3\xff!\xe7r\x96\x19Cc\xe3QZ\xf9\x93!\xec\x94\u007fPR\xe5T\xa1)\x18H\xd0\b\x10\xcb\x19R\x01g\u061c\b \x03b\xabPKpWK\x97\x8fJ\x1b\xe0\x9c\xf4\x14\xedv}\x14\xc6E\n\x14k\xf3b\f\xc1\xb4\f\xd7\b\xce\\\xb8\x17[g\xce\u00c5\x04\xa2\xf8:\x8cm\xe0\x9d\xe0\xd5W_C\xd7/\u007f>\xfel\xff\xee\xdf\xfd\x9bu1_\xaf\xb7\u007f}\xe4#\xdf#\x00\xf03\u007f\xeb?\xf9\u02fd\xbd\xbd\xeb\x8d1\x94\xe4\x99\xe1f\x9f\xddx\x1dG\xaf^\x85\x13\x0f\xb0Y\xc1\xbf\xd3\xc3\xe9\x1d\xc4wY}W\xe9\xb0\v\x81J\x10\xc2\xe815\x14\x1e\"\x8dm;V\xbbU\xc8\xd0]\xaf@/\xfa<8,\x83\x12\x12\xaf\xbb\xc4%\xe4\x14\xfa\x9fHE7\x8c\x83\xac\x92Yq\xca>TI\xe9\xb9W\xf6\x8aI\x83\xcf\xfc\x99\x9c\xe4\xfbC\xea\xdc\x18LR\x1f\bV>(m$\xcbPb\xbay\u05df\xbas\n\x19\x9e\u02c0\x9d\x17\x97\xc8\u012e<0\x91\xeaW*\x83\x83K,\xd82\u0632CQ\xa7!\x14\xb4B\x16\\9\xfdH\xc1<\x1a\v\xd2(0\x12\x80\t$\xa1|3\a\xf8#\x8a\u07b8\xf0K\xa6*\xfb4m\xac\x83\x81\x01\x11'\xd4=J\xffS\\!)\xa4c\r\xc1X\x02Y\x82\xa0\u01d9K\x97\xb1s\xf6N\xf5t\x8e\u015c\t\xcc\x16\xc4\x16\xf3\xf9\x1cO>\xf9\xd4GD\xa4\x01\x80\xcf}\xees\xebb\xbe^\xb7n}\xe8\xe1\x0f\xff\xdb\v\x17\xee|~oo\x17}\xdfgO\v\x01\xdcb\x86\xebW\x9e\x84[\xccA\u01aaA\u007f\xc1Z\xa0\xf4Pz8\xd7\u00fb\x1e+\x86\xaaT\f\xa0\xca\xce8P\xfe\xa2\xb8\x86\x9d\xa0=\u0283\xc5\x15\xf1G\x90\xf0\x93_\xc5P\xa4\xea\xe2\x8a?/\xa8\x8bc\xf6-\xabX\xb6\xa4\x0en\x98@A\x85\x95\x00\xb9\x82\xe5\xe2\xf2\xd00\xc1+.G\xd9}ckL55\xb0\x88\x1d\u0242\xab\x93DkH&BA\u9be3Mnd\x10\x85\x84\x9f(\xf6\xa2\xb7\u45a7\xe9\x04\xc6\u0520r\xcaah\xe8\xb78\xfc)e\xa4\x87\x8fT\xd9\xf45\x83M\x04\a\xa8\xab<\x19\u0122\x9aN\f\x12\x06\xf4Qz_@Q\xb2\x12*\x121s\xb5\xb5\xe0\xb09H\xe8V\x98\x04l\td\b\x04\x87\x8d\xdb\xce\xe0\u031d\x97al\x1b\xf2I\xf3\xa9\xc2X\x8b\xc5b\x81'\x9ex\xe2\xd2\x17\xbe\xf0\a\xef\x06\x80\xbf\xff\xf7\xff\xfbu1_\xaf\xb7\u007f=\xf3\u0313\xf1A8\xb9x\xf1\xe2\x1fmnm\xa6@\x80\xe8$\xd7-\x17\xb8\xf6\xdcc\xe8f'*\x87\xa6\u0324\b'\xe2\x80C\u01a3jW\u3728\xbb\xd3\xe8R\u0211\xd5\u0485\xa2\xe7\xb2_\xf7\xe4XM\x9ed\xa5\x98K\xea\x80\xe9\x14%\f\x8d1%\x86\xe1\b7+-C*x]*\xb3\xfa\xb5\x80Q\u022b\xcb`\x12\xfb\xf8\x82\xc5\"\xa7\x15lz\x8b\xc2.\xab\x88\xd1h\\\xdb\x10Y\xa2jf\xaa8x\xac\x87\x14:q\x1fT\xa2\x8a\xa3\x93\u01ea-\x02\x9dVvW\xdfJ\xa0\xa5R8\t\xaa\u079ep\x9a\xccW/:G\u0604\u00b9\xa0l\x00L\xf8\t\x9cT4\xcfT\u04a5(\xe2\x12\xc2\u028bBO#\x9d\u007f\x86Z\x8a\xcf#\x82p\b\xb1f=\x8cRCZ\xe4\x01\x90e\x9c\xbb|?\xa6[g\xe0\\\x9f\x8b9\x11\xd86Xv=^~\xf9\xe5\xc9g>\xf3\xd9O\xc5\xd7\xf8\x87\xdf\x04\xc1\x15\xebb\xfem\xbe\x1ex\xe0\xc1\xf4\xfeG?\xfa\xe1?\x9aN\xa7G\xc6\xc4\xcc\xc30\xc8\xf3\x1eG\xaf\\\xc5\xc9\x1b/\xa9\x10\x83\xb9\xa8)\x02\x1f\x06R\xca=\xef\xe1]\a\x1fb\xb6\xea\xb0\xc7\xfc\x9c\x97\xf1g\xe4<\xa8\xf3\u025f\x1b\"\xe0e\xe8\xd0\x175\xf7\x90\x12\xcd/\x17M\x00xK5\xcc\x18>]\xb8\x14\xd6|\xc4\u0560\x86R\x0e\x9e\xads\v\nf\x18\x8e\xa6H\xa8M\xd9\xc2\x00\x00 \x00IDAT8)\xbcXV\xeclO\u00cf\xdf\u00bf@\xc6\xe1\u007f9\xa5\xa9\x97B\xa8e\x96>\xd1\x15c\x88E3\xd37\x13\x95\x95n\x84\xbb=\x80\x97\x86.\x8b\xa5W\xfa)\xad\xf6MN\x1e#\u007f\x9d\xfcS\f\xc8\x18u\xe2\fp\x8a0\xa7\xcf\xe3\xa1\xe3b8\x19\x96\xfe\xfbz\x8f\xac\xc2\\\x91\xd1R\xe3\xff\xf9\xdeT>>\xc1\x18F\xc3\x046\x04j(\xcfTHp\xf6]\xef\xc6\xd6\xde\xed\x99\tC\xf1k[x\x0f,\x16K\xbc\xf0\xc2\xd5_\x14\xd1v\xe4\a~\xe0c\xebb\xbe^\xb7n\xfd\xec\xcf\xfe\xed/\xdc\u007f\xdf\xfd\xf3\xad\xadM\xb8\xbe\x17}(\x14W\\\x9e\x1c\xe0\xe0\x85'\x01\xef\xc0\x86S\"\x83\x88\x04?\xf3l\xbe\xe5]\x97\xa0\x96\x88G\xe6\u0388*\x0eu6\xb6\xaa\x8b#\xa0I\xf0\xed\xb1\x03\xf7z\x02PEcY@G\xf0\xd813\xaa\xb1z)\x03\u0339\xc4\ah\x1c+\x18\xfa\x81Se\xbaNE\x96\xe5[\xe1*CE\x10\x9d\xf2\xe7\xc5\xffK|\xa8\xe2\xce\xe5\x01\xb0\x9c\xe67&\x85\xd4\xdf\x15)J\xc9#\aI\xf1\xba\xf2\xbdG\xf6\x17\x19\x19gF\xd8'\xceB\xeaKwsc\x97\x15\x9az(\xe4\xc4\f\x84\x82\xee\r%\xd8#\xcd-\x80\"\x14e\xe0\u0452\x9c\xc5hpU\a\x11t\x81\x01C\x05\xc5QBRJct\xf0\u026d\x01Y*\xf2p\x1d\xb6\u039d\xc7\xf6\xd9\xf3`b\xddD\x8a\xae\xde6\xad\x1c\x1e\x1e\xe2\xd1G\x1f\xbd\xf8\xf9\xcf\u007f\xee\xaf}\xb3<\xdf\xebb\xfe\xef\xc1\xfa\xa7\xff\xf4\u007f\x8b7\xe3\v\x17/\xdc\xf9\xc5\xe9\xa4\xd5\xea\x1a\x8f\xad\x04t\xb3c\xbc\xf9\xcc\xd7\xe0\xbb\x05\x989\xfb\x92\xc7F,>\x04\u041b\xdd\xf7\u02d0+\xba\xda1V&V\xa5\xb1\xb6HV]\u01b4\xa2\x85Gs\uc495k\xeavob\xfe$\x83.x\xd4\xc1\xae<\xdd\xcbi8\xb5TM5%\xbea\u0465\xcap\xa3\xa8\xf39+S(\x19:\x0f\u07ac\x1b\x1f\xf9{\x1a\x9e.$'n\x14\x19\x11\xa7\xa62E\x05k\xf8\xa7v\xa16\xb3\x11\xb2\xc8\tH\xa74\xd3R\xf2UJ)S1\xad\xa6\xb7\x8a\x1b\x1a\xf9\xf3\xe1) u\xe2\x041\f\xd70<S\x1d\x18]\\\xeaZ\x80\x95\u00da\xa9\bo\x8e\xf7A\u2167\xaf%\xa9\xc9\xd0_\x9b\xde\xef&2X\x1a\x03\x9ed{\x00\x0e\xa6sv\xd2\u2d8b\xf7\xc0\xb6Suk\xa4lf\u01a6\xa1\x93\xd9\x1cG\xc7Gw\xfc\u025f|\xe5\xa7\xd7\xc5|\xbdn\xd9\xfa\u0407>\x94\xde\u007f\xf0\xbd\xef\xfd\xe2d2\u0566&\xa5\xa2\x13|\xdf\xe3\xe0\xe5+\x98_\u007f]\x99\x03\xc9\xf1\x90\xaa\"\x1d\xe5\xfd\xcew5v>\f/(\x9eB*\xe2\xdbb\x06g\xe4g\xb3\x13\xb43\x87\xf6\u0105\xa1\xe7M\x9c\xb8V\xa8\u007fr\xd3#\xbe\xd0\xcd\x0e\xffR8\rR\r\xe5$\xde{\x194\x11_\x17UFP\xdf\x10\xbcpj\xbd\xa3\x9b\xd7\u0081mp\xec\x8c\xc7\x10\x9c$\xc4q\x92=r\"\xa7<\u57be\xb5\xbeE\xc6@\x962TCd\x10\xed<\xdc\xedFH\xe8%\xbd4P\x01\xc50\x84\b\xce0\x96\x1b\x16\x8e\xf5c\x98\x907;2'\xc8E\x9c\x03\xee\x1e\xf9\xe6\x8cA\xe0g\xb5\x1b\xe4\xc6D\u007f\u007f\u0094\xbd]\x02\x8b\xa5\x1a\xf6\x8aR\x16\xcf\xdcu/\u068d\xcd\xc4j\x89\xd1sD\x1c\u063a\x1eW\xaf^\xfd\u8e98\xaf\xd7-[\xef\u007f\u007f>\t>\xf4\xde\xf7\xfe\xebK\x97.\xa1i,\xbc\xb8P\xd4\xf4A\x99\xdfx\x037^x*Q\xc4R\u0716\xe4\x87\"&O\x88\xef\xe1\xfb%\x10x\xe7\t\x8fL\x83\xa6\xa2\xc8\xfb\x12w\x8e\f\x11\x9f\x86\xa2\x14\x859\xc7N\xa9s\x812&\xb4*\nO\x1e\xd6TW{Yq\u05ebq\xf3\x9a\x98!#\x85\x8b\xc6an)7\x10\x1a\xc0\xf7rJm\x96Uo\x93\xb1#\xc3J=\x1c\x16w\x1a$,\x15\xc5\xea4\xdb\xdc\xc0\x8fO\xc3\xda\x12\x16\"T\xac\xf1\x15\xcf\xf2JDu\x9a\x1b\x1a\xa1\xb6>\xa7Qx\xa9d\xe0\xd0\xe0\x8ax\xd6\xee\xdc4\x16\xbce\x80-\x86oY?\x87\x00c\x18&\f\xe2I2\u03fb\xf6\uf29b\x8c:\x03Q\u0237\x8dM\xc8*\xbd_\xcd}\xa2\x10\x89\u00d1\xcd\x1b\xf5\x04\x8d3\xa4\b\xd1\x10\x11v\xee\xb8\x18\xc4C9\x10#\xfeLM;\xa57^\u007f\x13\x8f?\xfe\u0136\x88l\x03\x99l\xb0.\xe6\xeb\xf5\xb6\xae/\u007f\xf9K\x04\x00?\xf6\xd7?\xfdg\xf7\xde{\xf7\xd3M\xd3hDVR\xdd1\xe6\x87\xfb\xb8\xf6\xdcc qJ\x1b\xa3\x82\xf6\a\xa9\xfe\vx\x88\xef\xe0]W\xe0+EWKuzs\xec\xce5\xe5G\x87q\xa5l\x9ez\u581bE\xf0p\x19\xaad\xe8f\x92\x14Z%\x91\x8c\u06a7\x8eu\u01b2\x8aU\x8f\xd6\u027a\xf3\x8b\xf8\xf9P\x12Ie'\xff\x8d,\xba9\x12S\xa6\xf7\xe4\x14zI\x85o\x05\xe2\"*\x04WaP\xeb\xc7\xe0\xa2\xe1\xf5\x93\xe2@D\xd5\ub5caX(\xab\xf6\x03\xdf\u0211$\x16X\xd6n\xb8\x9dXl\xec\xb6\xd8>\xdb\xe2\xccn\x8b\xad-\x8b\xa6a\xb4\x86\u0476\x8c\xa6UL\xdb2\xea\xc0\x0fP\x96\xebG\x8f\x97\xb2s\xe7\x12#\xaf\xef\x17\x1f\u031c\r\v\x98\x00\t,\x96\xd8\x1c$\xff\xf3P\u0427{g\xd1n\x9d\xd1\xe6\"]\x1f=c\x18cp2\x9b\xe1\xf0\xf0\xf0\xae\xbf\xfa\xab\xbf\xf8\x00\x00\xfc\xf1\x1f\u007fi]\xcc\xd7\xeb\xed_\x1f\xfe\xf0G\x03M\x9b\xbaK\x97\xee\xfa\xbd\xe9t\xa2\x8f\xacw\xc1\x82\x9f\xd1\xcd\xe7\u063f\xfa4\x96\xfb\xaf\x83\xad\xc9).T\x14\xe4\xf8p\x06E\xa8s]H5/\xf3\u054ad\x9a\x92\xba\xe8\x8a.\xdd\xe70\t\ntE\xd3\x05VF\x80\n\x84Vs2\xa5\xd0\x12\xae\x18\xb7~\x03\xfc\xe9(u\x8c\xc3\u0541\x04i\xd0c\x16'\x848\x00\x1cD\xbfU\x1c\xc7R\x19:\xdc0d\x9c\xaaBc/\xa0\x1c\xfcRNQ\x1a\xe2\xff2,\xfcaX\xb8\xaa\x16- \xa14\a\xac\x8d\u0525\fz\xc6MR\xb6o\x86\xb9W\x1ba\xcd!\x8f~\xf0d\bm\xcb\xd8\xd80\xd887\xc5\xd6\xde\x14\xbb\x9b\rv\xce4\xd8\u07b6\xd8h\x19\x8d\x01\x8c%\xd8\t\xc3\x1a\x0e\x8c\u0161\xd9\x0eU\xb9\xaf\\\x9aq\x85W\x10\xa3\x12\x95\x88$A\xb6Oh,\x83\x1b\x02O,LksFn\x81\xbd{\xefa\xa6\x1b\xd8>\u007fIgH\xdeW\x87\x18f\x03\xef\x81\xfd\xfd\xfd3\xbf\xf3;\xff\xe2\x12\x00\xfc\xb3\u007f\xf6\xdbx\xea\xa9\xc7\xd7\xc5|\xbdn\xddz\xcf{\xde\xf3G\x0f<\xf0\x80>`\xde\x05\x99\xa6b\xc8'o\xbe\x82\x83\xaf?\acl\xa8\u0252\x1f\xcb\x02\x1fW\xec\\\xe1\x96\xca|\xab:\xfe\x8f\xb4\x99\x1e\x95?y\xea\"]\xc6\xd1c.e,\x9c\xe9\x9fS\u0462\x0f3\xccV \x90\\\xfch%\x03\x13\x99\xdf|\x1a\xb6]R\xe2X\v\x87p\x8ex\x13*,\vF\v\x1c\u0764 \xca*\x82Q\xc09\xaa\xf5\xa1J\x81+\x18\fr\x19\xc5F\xa3\xd7\xc93\xe0\rF\xc83\xc5FC#\xdb\x16\x8d\x1b\x98\xd1\n5h\xc0R\x1a3\xa3/60\x1a\x02YD\xb0\x960i\x18v\x8baw,l\xdb\xc0\xb0\x81\x99\x18\xd8M\x06\x1bE\xc4\r+\xef\x9b-\x83,\xaf\xe0\xe1U\xe6I:\xb5D\x99~hB0 \x06\x91\x84\xd9+\x81\f\x83\xdb\u0415\x97A\xd8\xe1g\xf0\xe2\xc1\xd6b\xe7\xceK*\xa4\xf3!\xa0%\u078fLX.\xbbn:\xdd\u061b\xcdN>\x01\x00?\xf1\x13?\xce\xefy\xcfC\xebb\xbe^o\xff\xfa\x87\xff\xf0\xd7\t\x00\xee\xbd\xf7\xdeG.^\xbc\xf8jc\x9bp\xe3\xea\x83\xc0\xd6\xe0\xe4\xfa\x1b\xd8\u007f\xf1Y\x18\u02e3]nv\xec\b\x959\xf0\xce!\xbe\xa0r\xdf\xe4\x1c.\xb5\xba3\xc2.)\xa9'\f\xeb\xa2\x19\x94\x84\x81\x95\u0400!B\xb4\xc2\x1d\xaf_\xaf\u0704\x0f]\xff\xd5X0\x03\r`\tg\x19\xae\x89\xafC\x8a\r\x81\x060\r\x12\x1bf\x15\xe2\x19\xf1\xfd\xa5\xba\x97EQP\xca\x19\xc4\xf0\u02d4\xbe&\xf1\xfa(;$~\\\x0fM\x85W7\x9b\xb1\xba^l)+\x93\x05\x1a\x83\u042bv\u007f\x15\x98!\xca\xef3k17\x13\x02\xefZ\xa0\t\x12\"c\xd5\x17h\xa3\x05\xb5\x16`\x1dp2\xb4\x93G\xfc\xfd\x13\xaa\xe3I9\xbbH\x94\xc4D\x81)\xb4\xa3\xe1\xda\x10)\x83\x85\x98@\r\ubc35\x8a\r,\f\xf5E@\xc6`\xe7\xf6\xf3`k\x03\xa6N\x15m\xd36\x8d\\\xbf~\x1dO=\xf5\xcc\x1e\x00\xfc\xf2/\xff\x1d\u007fxx}]\xcc\xd7\xeb\xed_?\xfe\xe3\x9f\x16\x00\xf8\u0527\xfe\xa3\x97\u03de\xbd\xed\xf7766re\x10\xbd\x1d\x96\xb3\x13\x1c\xbc|\x05\xdd\xecP]\xe9Rvc=K\f\xed9\xe0{H\x10\x11\x95\xb2\xe7\x8a\xee7r&\u03f0\x01\xe5\x80\xe5\x10t\x11y\xd3\xe4D\v\x91\x89l\x8a\xd3<ds\x95!\x19)\xd1R\xcf\xeah\x80\x03\x97\x90,\xa1\x86O\x84\xf5\xe5\xf5\ra\xb9\xc1\x81\x0f]\f\xe3\x88\u01bboz+\\y\x05W)\xe0eZ\x05\xb9K\u0590\xe4\xa1f\xec\xca]\xa3\\m\xcf\x04grO,\x85h\xaaJB*\x06\x895\a\xb5\xf4\xbf\xc1*]\x92\x86\x8a\xdb\u04f0\xad\xfaZ\x90!\xd8F=Px\u04c2\xa6&\x83Y\xcc0M\x03\xbb5\x85\u0759\xc2\xd8&\xd9\u0732!\x98\x86\xd57%\xe5\xf7\x15\x82\xa9(\xe5/\xb3EC\xb2\x90@\n\xd1\x18\x85\u0344U\xb6\xdfj\xd7\u03e7x\xb0\x8b\b\x8c\xb1\u063e\xedv\xd8v2\xfa\xb3\xc589\x10\xbdWD\xee\x05\x80\xa7\x9f~\x86\xd7\xc5|\xbd\xde\xf6\xf5\xe0\x83\xef\v\xdd\x12\xcd.^\xbc\xf4\xe5\xbb\xef\xbe\f\xef\x1c(\x8e\x86\xc2\xc4~\xff\xe5+8|\xed%\u0626\xa9\x8e\xcb5\xc3!\n3\x02\xd4\xe2\xba\x11\xbe7\xad\x1e\xc1\xa5\xd8\x1cbw\x9e\xe8\x8bH\x1e`\xc9\xe40\xc0\ae\x9ed\xecH\x890\xda[\x8f\xa5\xe3\xe4\xd7P3+F\x91!\xa9s%\x84\x00\xdad\u041e\x05My\x80\x9d\xbfU\xd1\x1e\xc3$V\xedmW\x93\xe6J\x04x\x10\xedS\x86'\x93@,\xc1[\x86\xb7\xe1\xf5\x18(\\a\x8aN\x1d\x94\x8a\x9a0\xadz\xbc\x9fB6\x1c\x8d\x8e\x1b\xaa\x80\xb0z]\x87\u078a\xb1(\x9b\xa9\x01m\x1b\xc0\xd2\xc0g\x8c`\xda\x06vw\x03vk\x03\xb6\x9d\x80m\x03f\x03\xd3\x18\xd8\u05a4\xa1<\x06\x8c\xa6\x12\xaa\x12\x91\xaa[\x8fX\xb9@\x87\x9e\x86\xb5\x90SC\x85\xdch\xe8p\xa9\x1b\x9ca\xc2\xc6\xd6.\x9av\xa3\n\xc8\u0223!\xa2\xae\xebqrt|\xf6\xd5W_>\a\x00\x8f=\xfe\u013a3_\xaf[\xb3\xbe\xf6\xb5\xaf\x12\x00\xdcs\xcf\xe5G\xee\xba\ubbbe,\xb0\"\x0eD\x8c\xfdW_\xc0\xe1+\u03e3i\x9a\xe0c\x11\x1e\x8eA\u01d6\xb2\x15\xc5\xc3E\x9f\x8c\x02\xfa .`\x05\xa2\xd5\xe3\xfc \u0693nR\xefF}\xb4G>Y\x04+_|\xb5\u03d5S\x99}eU\x8f\xe2\x92\u0254\xb1\xb9\xa7C\xba\u026e\x85m\xb9:\xfa\v\x8d\x9f\x16\b7{\xd1R\xc3\xff\x03\xa3/*w4:E\x84\x946\xa7|\r\x1a&lX\xc6dj\xd0L\x18\xb6\xe54|\xf4\xa1\xb8\xa7\xa4 \x1a\xbb\xe8\xe3q|+\xe3\x8f\x11\xf5\xd1\xc0\xb9<'%\x05\xac\xdcX\x02m\x19\xd0\x06\xe3\x14!+hja6\x1b\x98I\x8bf2E\xd3Na\xdb\x16Mka\f\xd73\x0f*7\xf6\xe8\x8cX\x9b~I\xc8\x00U\u02a3r\u02a9\xd5\u007f[Z\xd5P\xe0\x9e\x97,\x18\x81\xc0\xb4-\xectR\x04GSv\xdc\x14\b\x11\xe1\xe5W^Y\xfe\xee\xef\xfe\xee\x12\x00\xbe\xf4\x0e2Z\xd6\xc5\xfc\u07f3\xf5\x81\x0f|\x87\x00\xc0/\xfc\xc2/]\xdd\xdd\xdd\xfd\xc3\xcd\xcd)\xbc\x12\xcaCm ,\x0e\xf7\xb1\xff\xea\x15\xf8\xff\x9f\xbd7\x8f\xb7\xac*\xafE\xc7\xf7\u0379\xd6\xde\xfbtTA!\x14\x05\x04J\x1a\xa1 \x16\x02\u04bdkh\x05\x1b\x82\x1a\x8d\xb9\x11\x8d\x06\tQ\xaf\xbe\xe73\xd7\x17\xafz\x93\x97\u0118\xe4&\xf16&1/O\xa3\x98\\rc\xf3\x8c\x82J\xb0\uf0c8 J+}[\rU\xa7\xdf\u035as~\xf7\x8f9\xe7Zs\xad\xbdNU\xde\xfd\x03\xcab\xcf\xdf\xef\xfc\xaa\xea\xd49\xbb_c~s|\xe3\x1b\xc3\f\xfdh\u007f\xfa\xf1\x16$\xa6G>$\xd7\x06\xaf\xe8\xaa\x11Jc\xd2i\x8c\x01\xb6\xd4*\xd3v\xb8\x93\x8a\x86AK\x15\x9c\x0e5\xa1\xc9SW\x8d2i&a\xb4\x13\x1d5\xb9_\xa5\xbc \xe4\aht\xa6\x15\xba\x8a\u0419Q\xe8L+hE\x15\x17\xbd\x06\xc3R\u056bM\x93\u0644\x8a\x925\x0e\x13\xf1\xb5^\x83\u0268\xf9e\x85\xc6q\x87\x19\u074c\x90+\x82\xee1\xb29\x8d\xbc\xc3\xc8\x14\x95\x94QJ\xbbT\x81\r\xb2\x16\x19^\xc7\xf5V\xdf\xf5&]EcL\x98\xe2\x00\xa4=\x06\xcd(\u007f2h\v\xe0p\xfeT\x81N\xa0BXC\xe9\x0e\xb2\xbc\x87\xac\xd3\x01)\xed\xfd[h\xfc\xb5\xabT3TV\xfa\xc4\xec\x11Ny\x1f\x16\xad\x19\x943\x90q\u057f\x89\xb2D\xa1\xaa\x91\x1a\xab|\xeb\x13\x86T\xd6I\x9e\x8e\x84&h\xa5\xdc\x1a\x8eF|\xff\xfd\xf73\x00\xec\u0639s\x02\xe6\x93\xf5\u052d#\x8e8\\\x13\xd1#\xb3\xb3\xb37\x1cu\xd4Q0\xa3\x91Kydk\n\xcc?z/\x06\v;\xa1X\xfb\x12&U\x12D\x02@\xa8\x94qE\x99b*\xe1\x1a\xaf\x9c\x93d\x99\x9a\xe2\xa4}n?\x1eiym\x81t{\xc4|\"ii\x83\xee\xb5\x15\x8c\x95\xcf5sP]Li\xe8\x0342M\xd0\x04\xe4\x1d\x86\x9e\xd3P\x1d\xdf@s%\x8d\x91p4h\xabZe\x1c\xf2ZmZb\xe5\xdc\xd0\xc0K\x8b\x9fJ\xc2\xd9g\x8a\xd0U\x80V\x04\xd6\x04\x9e\u0460\xf5\x19t\x87\x91k\x86V\xa1R\xe6\u051aaO\xc6_\xe3\xc8\x1d\x1d\"\xd3MZ\xf6\xd0\x12\x90\xa0*\xc92\xf2\u0291i\x05\xear\xcdc\xb1^\xea\x87[\xeb\x06\xc0-\x15\x88\nJu\xc1y\xc7\xef\f@K\xb0E\x9d\xb6\x8a\xcf\u0445MKE3\xad\x9c|`\aPV\xe2>\x1d\xd1\x17&\"\x95\x04\xd79\x81\x90\x82\xeat\xab\t\xe0z\xe6(\x0f\x87#\x1cr\u0221\xcf9\xe6\xd8cO\x04\x80\x13O<\x91&`>YO\xd9z\xc3\x1b\xde@\x00p\xcc1\xc7\u0733n\xdd:\x80\xa0D\x9cP\x19@\xc0\xd8\xf5\xf8\xfdX\x99\u07c6,\xefTC\x87\x89\xee<\x1e\u0465\xf4\x96\xb65\x99\"\xb5\x8d\xaaK+\xea\xd6\x12\u071b\xd3\xf5.\x02\x9b\x932\x84\xbaV\xd1\u05fa\x9aT\x13z\x8f_\xe6k_g5W\x12\x02\xb2,4\xecf\x14\xb8\xabj\xb9\x11\xaa\xa7\xa0\xa6T5\x04\xd3\x12j\xbc7xlS+J\xed\x90\"\xf5\x83K\xa4\v$\xc5s\xffxY\x13::\xd2\x04\xf0^#\xd3\njZA\xf5\x14t\x06t\xb4\x0fapLp\u068f\xd3\xd7\x01y\xcf\xc3U\xe5\xc9E*\x8bc\xec\u1d4d\xbd\x06\xa5\x00\xce\x18\xd4U~\u0693\x9atT\x83\xaa\x12\x01r\xf2\xd59\xa7\x16\x02\x04h\rR:\x8c\xf3S\x12#\xc7`j\x8e\xf4'v2\x01\xb8UG\x81s5v\x1ap\xceS\x85Rg\t}\xea\x96R\xe0\xbc\xeb\x1fZ\f\x9c\x8e\xb1\x89DT\x14\x85\x9d\x9d\x9b\xa5^o\xea(\x00\x98\x9b\x9b\x9b\x80\xf9d=uk\u077a\x03\x1c\x00\x9cw\xde/\xdc\xc1\u0337\xe7y\x0eg\v\x11g|\xe3Gi,\xee|\f\xbb\xb7=\xe8\xa5cTM\xdd\u0544\x89R\xc9\x1a\x05\xce'\x11\x89\tm\xa7\x14\x8c\u0491Lj\x16\xeb5\xee 6\x16IU\xe0\xe4\x1d\x15\xa9\x15\x1c\xc7i\x1a)9\x01\x19s\u05d6\xf6\f\xe5\u06a4\x8f\xe7vuF\xe0\x0e\x81g\x94\x97\u01e5A\u011a\xa0\xa6\x95\a\xa9\xb4\xa1\xb8FSQ\u068e\x0e-\x9e\\5\xb9dT\x0fQ5\x06Z{\x96\xe4\x1b\u00e4\x80NF\xd0\u0468J3h\x86A9\x81\t\xe0\xf085\xc3\xd3/Lp\n\xb0:q*DK?\xa39\xfa\xb4\xc6ND\x8d\u05d6\x92\x97\x93\x13\x9e\x9a\xa7\x14\xa8\xc3\xe1\x90Q\x97s\x96\xbe)\xb1\x9bA\x04\xea2D\xa1\x8cx\xf3AQ\x1cr\xf0\xc2\u0670T\x1f\xb5\xbc\xfe\x8cr\xa8\x88\x99\xa02\xdf\xf8\x14\xaa\xac\x1f\xc4:\xd8d\xb2\x13hX\x00K\xa8\xcc\xf3^5\x80\x84T\xc1\xe4\xc7\xfe\xad1\x18\r}\xf8\xeah4\x9a\xd0,\x93\xf5\u052ds\xce9\xc7\x01\xc0)\xa7\x9cv\xffA\a\x1d\xf4\xc0\xec\xcc\f\x8c1\u039a\"8!\x12\xcch\x88\xed\x0f\u078ea\u007f\x1e*\xcb\u00a7%H\xbe\\\xe5']\x06\x068\a\x17\xc2+\x90D\xb6\x8d\x83\x9c\xd4t\xd4\xf5\u03a4\xd7\x00\xebL!\xef0\xf2\x8e\x1f\u91a2\xbaN\xba\xea?%\xf2f\xaa\xf3\xc92>u\xb9&\xa9\x90L\x161\x03:\xf3\x928\x9a\u0460\x9e*G\xe3\xd3\xdf\xd5S\x8c\u038cB\x96yN\u0595\xd4\u0178;\xc9\xda>-\rZ\x02i;\x93jz\u9997U\u0515g\x9a\xa1\x94\x97\xfa0\x13\xb8\xcb@O\x87\x14\x1f@r\x0f\x8c\xc4\x04M\x9e*\"\xf2rO\xa7\xa9\xee\xf9\xbe'=\xa5\x94n\r{5*\x88'\x18\xad\xbd\x82\x85\xbb\xcaS,\x94\x06\x83H\t\x98\xfe-Hc\x00}u\x9e\u6002Q\x99c\xc5Sa\u0512K\xf2z5\xe8'o\xc9\xe2)\x16Q(\xbb\x9eN\x04\xce\xf9l\xd0\xd42\xb7\xa2p<\xedb\x84\xa0:\xbd$\x90C\xea\xc2\"\x11\xb2\u01a2(F\x02\x00\xa6(&`>YO\xdd:\u3333\u4aab~C\x11\xd1\u02a6M\x9b\xee:l\xd3a0\u0188\xb3\x06\u058c\xe0\x9c\x05k\x8d\xc7\xef\xfd!\x16w=\x06\xdd\xe9\xc2\x11`\x93(\xb3\x94\xe2\xf0 \xee\x12C\xa2\xb5\x9cI\xea\xf6\xb3c\xf0!^>\x96k\xdf\xc0\u02fb\xaa\xa4\x0f\\\x18\x84\x19\x93\xc6Q\u04c0\xbc\xc1?\xb7\xc8\xeeh\rV_\xe0\xc7\xc8YyP\xe4Y\xed'-\x93\xe9\u0258\x80\u011a\x90\xcfhtr\x86\xd6\x04\xd1T\x8d\x9c'\r`\u0683\x9aE\x92>\xa04\x8d\xbbHZ&\xe5+\xc0\x12\xf6\x01\xc4Y^\xa90\xa8C\xa0\x19\xe5\xb9\xe18\x80\xc5\x04\xf4\xfc\xf7\x88\xc3\xfb\xe7\x82\x12I\x13D\xb7\xbc\n\xb2vw\x81\xd0\xf4\xea\x19\u007fK\\\xb2)\xaa\x0eC\xcdy\xae\u0725\x80\x99(^\xa89\xd1+~\x03\xa7\x8c\xc0\x91\xdf&\xa0\xa3\b:\xba+&'\x99\xca\x06(\xbc\a\xa1\x8a\x8e\xbf\xc8\xda\xd36\xfe\xbd\x8c6\xb9\xa86\x11\xa9'\xa1\xc6\x13\xd1\xc8:Xbd\u0769R\xcbN\xa9\xe82\xa6T\x85\xd4#\xff#n\x02\xe6\x93\xf5\u052e#\x8f<\xd2\x01\xc0\xb1\xc7\x1e\xfb/Z\xeb\x95,\x8c\x83\xfa\xe1\x1f\vf\x85\u015d\x8fa\xfb\x83\xb7\xc3:\x03\xce\x14(\v\xfa\xdc\xd8LK\xa4g^\xa2hC\xac\x9c\xad\u05ffM[\xd2\x16 M\x1cR\xbdicO\x81\xa7\xb5\a\x84\x10\xc2.k\x17\x8d\xb5\xbf\x8c+\xa6\xa5\xb5:o\xdeH\xa4\x05\xb4\"\xcf\xefv\xa8.QoT\xa6\xdcc\xa8)\xafl\x81\"X\x15\u031b\x90\xd2Bk\xf9\x9cP\xc3tP\xd6d\x9fR\x18\r4/\x90\x11\xb2\x8e\xaf\xca\x19!\xfalZ\x81z\xaa\x9a\xf0\f\x0f\x96;\f\xee( $F\x95U\xb6\n\x03F\rm\xfd\xf8\xabKk\x9cp$y.u\u0263V\xecO8=\x06\x05\xae\x9c\x92\xa8\xc2\xf4P4.\xa4\xf1o\x06u\xd9[\xd4R\x10\xa5\xc0\xf7?)\x9eLP\xc9\x05k\x9f5\xf6\xef\a8|V\u00c0\x10$-\"\xa8Rf\xc1\xf7c$\xfa\xbd\x130\xb4\x82\u0551\x83\x03\x81\xb3,\xf9]i|\xac\xbc1\x1c1=\xed\xd7\xf4\x04\u031f\xa1\xeb\xcc3\xbd\r\xf3\x9b\xdf\xfc\x96\x1f\xaf_\u007f\xe0\x83\xddn7T-\x0eb\x8d\u709d\xc5\x03\xb7}\r\xfd\xe5\x1d\xc8z=pF\x90\x8e\x82\xcb\bN{\x1f\x10\x1f\xfb\x85r\x04\xdaK\x14M9\xfe\\\xa75\xc6\xf5\xe0m\x93\x98\x1c\x9ax4\xa7\xc0=\x0e2\xc0\xa4:o\xd8\xd9\x12\u045e ~\x8f\xf5qZ\u1ae0\x85\xe6\x1e\x033\x1e\xfc\xaa\r\"5\x8f\rn}\x99o4R\xee\x83\x16\x9c\n\x13\xa2\xdc\u019f7\x06i\xa8v\xa6G\xb3!\u062c<\u02de\x02\x00\xc9\b\xbaC\u0202\xf2\x8e\x18\xa0@e\xa0\x1e\xcf\xe9\xa1J\x11\\\x8fa\b\xb0\xae\xbe)\xfaf\xe8\xb8\u007f\xb8\xb4\x9e[\xc6#D\x9b\x9a\x17\x9f\f\xe77\x1a\xee\x12hN\x039\x97\xbf(\x8d\x91\u007f\xa1\x9a\x8d|\xf5w\x11\xff{\xe1I\x92?\x02\xfa\xd70\xce>p\xdd\n\xd8KE\xeb\xd6\xc9\x16\xe13\x8a\xd8@\x0f\x95x\x00\xef\xf8\x15\xef\xdcY\xa0?tX\xec[\x14\xd6\u007f\x86I\xe9F\x82\x91\xff\U000948cf]t\xd6w\u775bT\xe6\x93\xf5\x14\xaf\xf3\u03ff0\xba(\xfed\xfd\xfau?\u0770a\x03\xac\xb5\xe2]\xf7\x83\"\x85\x15\x9e|\xf8n,l\xbf\xcf\x1b\xf8+\xf6\x13\x99D\xb0\f\xd8\f\xb0\xba2w\x92\xc0=W\x9as\xc1\xda\x01\fI\x85\x9aL\xceHP\x88P\xa0:hFA\xe5\fR\xf1\x18\xbd7\xb6\xb6\x9c)\u017f\u05a7U\x82\xdeXi\x02g\x04L)\xa0\xa3\xead1\u01951\x04?\x19*]*9WI\xc3\x15\x924\xf9\xba\xfa\x1c5\x0f\x1bYc\u04d1f\x0f\x00\xa1\x19\x9c\xb3w\xfe\x8bw\xa1}\xf5K\x19\x97A\x105\x13*\b\\N0\x8a`\u04f4\xa1\x14`\xa9\xfd\xddi\xdb\xf4Z\u01cc\u04aa<#p\x87\x81\x19\r\x9aR\xa8\xfb\xa47NQ2~\x88+\x1fZ9\xe0\x13\xdfW\xff\x99\x92`(\u6e1a\ue31f\x1b\x0f\u8f8fc\xadC!\x82\u00b9Z\xfb\xa44\xe5\x92J\x8e(\"0\xd6ayP`a\xa5\xc0\xa8\xf0\tMD\xde;\xc67\xfc%\t\x88\x0e-Sq\x12\xa8\x97,\\O\x135\xcbd=\xb5+\xfd\u031dx\xe2\t;gg\xe7 \"\xe4-C\xfd\\=+\x85a\u007f\x05\x0f\xff\u4ef0v\b\xd6Y\xa9.\x88\xb2A\x973\xac\xe6Ro]F\xd1%\xdaqjuR\x94\x1aE[ce\xb9\xb27\xa5)\x055\xa3\xa03\xafn\x88\x95W\x1d`\xd0N\u153a\xe0\xbd\x1bps\x1cl\xe9\x84\nW%\xb7/T\x9bq\xa9\xf5\"\x15\x83\xa75(\xe3j\u011f\xd1Ry\x03k\xfd\x8d\xd6<Q\x8c\xd3,\xa2}\u056b9\xe8\xa5\x15\x01S\f\xf4T\xf3e\xadQX\x96\t\x83\x8c`y\x8di\u073d`P\x05a\x82f\xeef\xfa\xa8\x15\x03y\xe6O74\xa7\xab\xb1\xfd\xe6)\x85ZB\xa4\xa4\xa5E\xd0\t\x05D\xb0\x9appp\xe4`\x15\xc1\xe4T:E:\x9d\x18\x8cY\x813\x0e\x16\x80!\xc0\xd80\xa1\x1cA\xbc\xe4\xcc\xfdk\xe1\x9c`0tXZ\xb5XZ\xb506\x1e4%\xc4\xd4E)dj\x17\xe0\xdf\\fR\xd6YdZ\xef\xda\xcb\x1b8\x01\xf3\xc9z*\xf8\xf3#~\xa0\x14\x17Z\xab\xa4j\xb1 Rp\xce\xe2\xf1{\u007f\x84\xfe\xfc\x0e0\x14\xb8\x1c\xfc\xf1U\x95U\x84\xd14\xc3LQHm\tIDh6\xbb\x1aR@\u051b~5\xea3p\xcf>4\x83\xa0f4\xf2)\x15T#\t\xf0T\xa7\xe6\x86lN*\x15H\v\xc5\xd36d\u009a\xc19{\xf9\\\x87\xf7\x98\x19\x9dn\x16\x04\x01\xf7\x18\u064c\xaa,\xb7\xa3\xbae\xec\xfe\xd6\xf2i,\xdd\xd3\xf7t|\x800Aw\x152\xe5\a\xa9\x88\b\x92\x93\xaf\x80U5);v\x86\x10\u007f\xa5\xbb.\xc3i\xef\xc4S\x029\xd57\u023dGUS\xe3tB5\x1f.\xad\x83zeV\x83:\x9c\xf0\u048d\xd3F\xe3\xce\xd6\x1cD\u04be\xdav\xe2`\xc5\xc1\x88\xf8\u07ae\x02l\x87\xe1\xf2\xe0K\u00fe\xf9\u8303-\x1c\x9c\x15\x18\b\xac\b\xcc\xc8a4t\xb0&j\xca\x05\xd6\n\x8c\x15\f\x06\x0e\x8b+\x06\v+\x06\xab\x03\v\x91@\xb7\u01e0\n88[T\xba$\xaaNP\xd69\xe9t;\u0635{\xd7\x03\xfdA\xff\x16\x00\u063e}\xbb\x9b\x80\xf9d=m\xeb\xdcs\u03fbijjzg\xa7\xd3A)7ta\u0519\xbd-\xee\x8e{o\v\x12=F\x9a,L\xd6\a\u43ba\x04\xa3\xf7\xe0\xf3]K\x9f\x97\x96\x01\xa2\xca\xdd\xce{\x88$\x84j\x87\x90\xcd)\xe8\x8e\x0f\xfeu\x8d\xea\x97\x1a\xb7O\x89- \x89\xec\x85b\t:\xe4\xdc7\u0290S\xf0\xcfn\x199\x97\xf6\x84#\u0584,\xd0Aec\x98\x81\xf1\x12to\xdc\xfe\x9e+c\u02bd\u05ca\x8a\xaf?\u00df\nr\xaei\xb5\xdb6\r\x06!\xeby\x8e?\xea\xf7K\x1fo^\xab\x12o\xb6\x8eS9\u0478\u007f\xbc\x1f\xcca\u042c\x02\xa6\xfdI!\x86HS\xb2\x11Hi\xd4YW\xec\xa4\xf3_\xf1?\x1c\x93?\xf9\xc1\xf7s\xac8\xff\xfeG\x13\xb1\x98\x02d\x1c\xc4\xf8\x11|'\x9e\x901\x01\xe0\v#\x18\x0e-F\x85Aa\x1c\x8c\x15\x14F\xb0\xb2j\xb0\xb4R\xa0?p0\xae\x1a\xed\x0f\x01\xb9\xfe\xb1:\a[\x8cJs8F\x95\xc0%\u21080\u055b\xeao\u077a\xb5\x0f\x00\x87\x1f~\xf8\x843\x9f\xac\xa7o\x1du\xd4\xe6\x1b7m:l\xc7\xdc\xdc\x01\xb0\xd6W\xd5\xe2,\x98\x00\x9de\x18\xae.\xe1\xf1\x9f\xde\x02\x16\v\u036aVF\xb1\x03\xf4\u04075\xdb\xcc\xf3\u65774j\xe0\"\u0524F\xa4V\xa9I\xfcD\xaa\xc47<\xdc\x17Okd3\n\u0228\xd6\xe8\xaa\xe1\f\x8d\xfbp\xcbX5\x99\xd6\xc2\xe1\xd7\x19\xe0\x8c@\n\xbe2\xe7\x16\xf0M\x1c\x1e\x9b@\xcc\xe4\x95-\xd2\xe3\xaa\u008d\x8a\x8a1\u007f,\u00b8\xba\x86Z\xcf\x0f)\xb0\x11\x13\xb2\x0eA\xa9\xa0\x9bf\x02z\xec\xf9}\x1e\xefC\xa4\xb5\xb3\xf8\x97\x14\xbd\x8c\u045b\xd3\xe8L)t\x82>=\x866H\x8b\x87\x0e\xed\xf5TQ\x1f\xb5\xcd4AM\xb1\xaf\xca3\xaeM\xbf\x8fW\xf4\xf5\x9eE\x9bs\xbb\x930\x18\x95y\xf9eMJn\x05\u0537\x90\x91\x85\x14\x0e\x12\xaa\xf1H\xa1\x18\xe5i@\n!\xcc\u00d1\xc3p\xe80\x1a9\x8c\n\x87\xfe\xd0a0r\xb0\x12\xde\u007f\x92@[\x95.\xbb~\xc3q\x16f\xb0Z\xf5\x17\xe2)\t\x028G\x04\xe0\xd0C\x0f\xd5\xcf\u007f\xfeY\x1a\x00\xb6n\xdd:\x01\xf3\xc9z\xea\xd7\xea\xeaR\u025fo\xde|\xf4\xad\xddn\xb7\xf4\x87vA/\xabX\xc1Z\x83\xf9m\x0fb\xb0\xb2\ryG#SA\xcd!\x04v\x04e\x04\xd9\xd0_\xfdN\xc5\xea\u070d\xe5\x97Q\xb3\t\x88(WKR\x85\xd8?\x1e!\n\xd3\xfbA/\xad\b\xd9\\\x06\xdd\xf5\xd5y=\x88\x81\u01a3gbe-\xcd\u071b\x06\xe5C\x04\xce\xd9Osj?%X\xaf\x14\xc7C\x96K\x15\rR\xab^\x82\xe91l\x90\xff\xf9F\xf1\u06aa\x90\xf4d\x10gU\x05kW\xc8\x1c\x1c\x10uTph\x02M\xd79\xe9\xb5n!\x16\x9b\x19y\a\xc8|Z!\xd7\u0790K\x01Aw\x1e\xbcT\x80\x16E\v\xb5l\x9a\t\x81\x15$\x9dY7\xf0\xe4=\xae\x01\xb4\xb4%\x12\u057a\xa6\x91\xb0\xa1\xb1S\x013Aw\xbd<\x95\t\xd0\xca\a\x99\xa0o\x81\xbe\x81+\x9c7\u010a\x95\xbe\x00\x8e\x04\x96\x91\x18i\x01\xd6\t\x06#A\u007f\xe8\xd0\x1f8\x8c\x8c\v\x1e\xe7^\xd6\xc9YH5\xe2\xaa\x1f\xa0\x14C\xac\xc1p1\x04N4\xe4\x87\xceY\xd7\xebu\xe1\x9c\xdc\x0f\xe0^_\x99o\x9ap\xe6\x93\xf5\u052f\xa9\xa9\xd9\xf2\xef[\xb7n\xfd\xfa\xd4TO\xb4\u05be\xba\x15\a'\x16\xcc\f\xa52,=\xf9\x04\xb6=r;\xf4\\\x86,gh\x95\x00\xad\x13pa\xc1C\vK\x0eF\t\f\xd5u\xbbcQo\xa9~;\xb5\xa4\xd5\x04d\x1e\x10kV,\"\xd0S\f\xbd>\x87\xed\xb2\xd7GG:\xa6v\x1f\xd2N\x8d\a\xb4\x8a\xfe&e\x85\xa7\x025\xa0B\xfa\x8c\xa65=KJxJ\x9bg%\xb5OP=\x05\x9aU\xde\xe8JUz\xfc\xb6\a\xd4v\u016fE\xc2pF\xd0S\f\xa5\xa9<I\xc4\xe9\xd4:\xf7\xd3\xc2S40Xk\x82\x9e\t\x92O\xae\u0326\xa2\x13\xa1$\x00M\x8d\x8a|L\x16\x9aL\x8ef\x9a\xc0\xb3\x9e+\aS\xddL\xad\xa5\xd3Y\u007f\xc8U\xd5-\x8d\x82_qP\x181\x81!\xc8\bPp\x80q\x80\xf1\x9f\xb1\xd2R\"d}\x9a\xa8\xa7\a\u00a0\x92\u007f\x8e\xd6\t\xac\x00.9m(\xe5\x03+\x94R\x89j&l\x80Z\xc3\x16C\xf4wm\xf3\xb2HR5%\x901F\xe6\xe6\x0e\xc0`0\xb8\x8b\x88\x16^\xf4\xa2\x17\xd1s\x9e\xb3e\x02\xe6\x93\xf5\xf4\xae\x8b.\xba\xf0[\x9b6m\xea\xfbJ#L\xb39\u007f\x06\xd5Y\x86\xc1\xd2\x02\xb6?t7\xa8\xe7)\x0f\xa5\xfdD\xa1\x8b`\x12\x02\x9a\xc5:X\x16/\x0f#i\xaf\xeej\xff\xac\x1aK^\x06\x16\xbc<\x12<\x90\u0130.\x9bUP3\x1aN\a\u007f\x91\xf0\xe5\xe5\x80M0\xa3J\xcb^\xa59\u051a}\xde\x03&\\\xc7\x1d.\xb5\xe5cvYM\xb5E\x02\x98\x04@30\xdde\u032e\xcb05\xad\xd0\xcd\u067b.\xeax\x8c\a@\xe3\x94\n\xed\x85='\x00\xaa\xe3\apJ\xaf\xf3\x0e{\xc9\x1f\xb7\x19\x8b7\xa7\u007f\xa8\xc6r\xabP\xe5\xa3\xe7\x03\"4\xc3\xdb\xfc\xe6\xec\x83\x1bH\xb0\xc6\x1b5\xf6\xc0\xe2T\xacb@\a\xf5\n\xe5\\\xdf\\\xa4\nInk_4\x03M< \xfb\xa5\x90\xf4<4\x95\xbe\xe5*L\x8dJ#W\x02\xc1\xb3\u0769\xd4\u0650\xab\x93\x14\xa3\xac\u0115\"?\x90\xa6\x19\x14\x9c\x18\xabM-(X\x98P\xf4\x971\\\x9a\xf7\x8d\xfd\x84\xa4\x13q0\xa6\x90\x03\x0e\x98\u00f1\xc7\x1e\x93\x01\xc0\x05\x17\x9c\xff\xb4N\x0eM\xc0\xfc\x19\xbe>\xf5\xa9O\x00\x00\x0e<\xf0\u0ece>\xfa\xe8Gz\xbd\xa9\xd2\x19.\uabd9\x15\x9c5Xx\xfca\xac.<\t5\x97\x83\xa7\x95\xf7\x87\xa6DE\x1d\x91\xd7Yx]\x8b\v\u0375Jw\u0754\xe1J\xf4\x91F\"\xb5K.\xc6t\x06C\xc4k\xc1;s\x1a\bt\x8bS\x1c\x86\x98\x1aa\xcb\xdc\xc6\xf3\xd63D}\xfed\x18\xf7\u05be\xf9\u064c\x19m\xe3\xaf\u04d4\xe5X\xf12\x019\x13z3\n\x9dYo\x99\x9be\xdeF\u05c3\xba\xf7\x15'\xaa\xb3\xe5BX\xdb\xfa@<_\xac:\\\x05\xd1k\xf2~1\x19\xd5\xf8\vj4&\xdb\xe8\xee\x9a\xe8\xa7\xe7\xd5;y\xc6\xe8jF\xce~\xf2U1\x1a\xfd\x8c\xb4\xdf\xd14m\xa7\xd2aR\xcdi\u040c\xae\xc8\xee\xb4\x0e\x97\xba\x15\xa646E\x8a:\xf1\x04\x94H\xbc\xc4\x10\x85\xf3\x95wF\x1e\xd0\u00f0\x90K\xbd\xde\xc5\x1bq9\x82\x9f\u007f\xa8\x92\x80|SVy\xb5\x92\xf7\xdca\xbfy+x\xbf\xf3P\xb4H\xd2]\xf1\xc6\\\f\x81C\u007f\xd7v\x14\xfd\x95\x00\xf0\x15\xc5\x146!\xdd\xe9\xe48\xfc\xf0\u00df\x04\x80\xa5\xa5%\x99\x80\xf9d=m\xeb\xe7\u007f\xfe\xe4\xf2\x83\xbfn\u077aok\xad\xc1\xcc\xe5 \x85\x04/sR\nK;\xb6a\xf7#\xf7C\xe7\x19x\x9a\xa1\xa7uh\xa2I5=\n\xf1\xde\xe6\xc5\b\u018d\xe0\xc4V\xa1\x01\x81\u05e6\xf4\x9c\x9e$u1{p\x85\xae\x18\xe4\x88\xe5Qz\xae\x00t\xa7\x14\xbas\xba\xfc\xf4\nU\xea\x86X\x02S\x8b\xdab\xac\xc2$T\x94H\x1e\x00=\xa1d\x9aus\x9b7z\xfc\xa9r \x91\xbd\x15\x80\uacbf\xed\x8c=8\x04\x1d\xbbR\xe3qw\xed.\xe8~\u0491;\xde\xf5\u0417\xaaa\xc4}\x8a\x13\xfb\xef\u0118\xab1\x8b\x19\x03\x8e\x13\u0303u\x1ek9\xf7\xb6\xb4J\x91\xa72\xbc=;\x14q\u02f3\x8b\xef\x1fjS\x9b\x9e\xd3\x06\xf4\x8c\x02\xcde\x1el\x91p\xea\xd4n\xddPg\x81\xa8\xfcQ\n\x95\xb8\x98\xa0N\xb1\x0e\u0384\x93F\xb0\xc5\x15M\x18\x8a\xc3\x10\xe2O\x80\xe1\xb9\xfbA6\x82\xe1\xc4m\x92P\x16\b:X\x003W\x95\x81\x88\vCC\u0276\x15\x1d;\xb5\x82X\x83\xc5\xc7\x1f\xf4j\x16nD&:\x8b,\u02f8(\xcc\xee\xf5\xeb\xd7\u007f\x1f\x00\xf2<\xc7\x04\xcc'\xebi[\xc7\x1e\xfb\x9c\xf2\xef\x1b6l\xf8\xce\xd4T/\xc4sUm(\"\x06\xeb\f+\xbb\xb7c\xd7#\xf7\x81Y\x815C\xcdh\xa8\x9e\xaa\x86)\"\u8245\xb5#\x98\xa2\x8fQ\xb1\nk\x87\xde\xf3\xc5\xd9\xd2]Q\u0106/W~1I\x98\xf4\fc\xd2\xc1x\xabn\x80\xe4\x03\x18z\xebrds\xda\xda\x15\xcc\xfa\x00\x00 \x00IDAT\xcb\xd4J>\xc2S(\xd4\xdauk\xe14\x94\xaf|\xbd\x8b\x93o\x80\xd6+Q\x19\x9bYoU:\x96\x9bT\x00\xa5.{\xfe8\xfa\x83D\x80\xe1\x90A\u065a\xd8\xd3(\xa6\xa3\xe41\xe7\xb0?y\xfa\tS\u028f\xb8'`\xd8\u6552\x9ef\xcaSN\xf2\xbf\xc2~z\x95\xf3\x8af\xa2\xb1I\x9e\xda!\xa1\u04a4'3\xa6Y\x97\xc1\xeb3`\xca\xd3@\x92h\xddkV\xb7\x90*\x90[\u0195;\x12\x93}\x8ad\u01f1\x01t\x9d\xaf\xdc]NXq\x82%\xe30\xd4@\x911LF0\xda\xcbb\x8d\x06\xa0\xb8z\xadu\bmV\xfe\xab\fW\xa1\u051f?$\r\x05\xc7N\xef\xb2K \xa5`\x8b\x11\xe6\x1f\xbc\x1b\xb6\x18\xfa\x1f\f\xca\x18\t\xee\xa0J)\x1c|\xf0\xc1\xc3\xcb.\xbbt\x01\x00\xce>\xfb\u0327\xf5Z\xd6\x138\x9b\xac\xb8\xba\xdd\xfc\u01a3\x8e:\n\x8f>\xfa\x18\x86\xc3a\xe9\x12GD\x10(\x8c\xfa\xab\x98\u007f\xe2\x11\x8c\xfa\xab\xfe\xfb\xcaA\xcd*P\xe1\xe0\xfa6\xc4o\x95\x1a\\X;\x80X\x03 \x03\x93\x06\xb3\x06\x83\xcb\v>\xba\x06z\x05\x01Cu\x14\x9c\x10`\x05\xb0\x9e\x1b\x85b\by\xef\x13J<NX\x13:\xeb2\xb8\xa1\x83]\xb1~4\x1b\x16\x10Sj\x9b\x01\n\xa1\x05\xc1\a\xbb\x96_\x1a+0\nc\xe3\\\vs\x1e\xab\x96\x13\xa3\xf1T&\x1f\xd5?\"\x94\u0394xN\xba\xe3\x00c=\b\a\u037bP\xe5\xe1\x1d\xd52c\x03VqX*4f\x81@\v\x84\xfc\xcc\xd2\xc6\x11I\x0f I)\x82T\xf1~M\xd59%\x9b\x12\xe5\ft\x150t\xe5k\xe2'|\x1but\x12^MU+\x02Z\x13\xb2\x034x.4=\x1dJ\xce]Z\xec\u039c\xf3\x14\f\xd9T@*\x89s\xa4\x84\xea2q5\f7\xe0\x9c\xc3\xca\xc0`\xbeo0$\xff\x9e9\xe7sf\xbd\xef\xb9\xe7\xe7)\xe1\xc9#`G\xc6Mhl\u05b8z)\xc3\xc90\xca>Ii\xac\xee|\x02\xcb\xdb\x1e\x868\a\xa5\xf32\x95HB\xf3s\xe3!\u03e2\xf5\xeb\xd7\xfft\xfd\xfa\r_\a\x80\x13O<Q&`>YO\xeb\x1a\x0eW\xd1\xe9La\u02d6\x93\x16n\xbd\xf5G\xcb\xff\xf2/zf8\x18\x02\xf0\u04c2\x95\xf3-ai\xc7\xe3X~r\x1bf7l\x84+\x86\xd0\x19!\x9f\xd3\xe8;\x81\x1b\xc5\x04\x1a\x02\xc8\xf9\v\xac\x18\xa1pE\x88\xfe\xd2 \xe8\xb2\u05258%)\xe4+'\u0470\x8e\x80\x91\x03\x93?:\xc3V\x01\x10D\xf5 `\u0342l\xda\u00ac\x8e`\n\a\xb2\x0e,>$\x83\\\xa4\x05\x18\f\x05\"\r0WG|\xf6\xea\x15\"\x04\x8a\x85\x92\xe0\x04)C\x12j2:T\x91b\u0360\x8bT\x16)B@\xc6\x1ex\x87\xce\xff\xdb\x010\x91\xa2\xf0\x95\xa6OWJ#\x86\xaa\xe3>\xa9\xb0\xc1\xc4\u02b1\xc3\xc0\xb4\xcf\u03ecB\x9fi\x9cFj\x99\xb8tR\x0fe\x02\x00\x0e\xaf\x81\xf4\x18n\xd9\u07ce\x15\x81qRZ\xfd\x8e7=\x93\xb4#&d\xd3\nj]\xe6O\n\xae\x9e\xa3]\xca\x12]\xe0\xb3\x1dPX\x815\x0e\x19\x01\xb9B\xeb\b\u007f\x93\x11s\x02\x14\x85\xc5\xe2b\x81]\xf3#\f\v\a\x8aJ'?\xc5S\xc6\xd3\xc1Q2\x1c\xdc4O\xf0^+U?\xb7\xaa\u04a3\xcbbT\x1f)\xad\x01g\xb0\xed\xf6\x1b1X\xd8\x05V\xc1\x1f>\xbc\x98\xe2,\x9c\xb54w\xc0\x1c6o\xde\xfc#\n;\xd8!\x87\x1c6\xa1Y&\xeb\xe9]\x9d\xce\x14\x00\xe0\xbc\xf3.\xd8\x01\xd0u\x9dN\x0eV\\\x82m\xe4]\x89\x19K\xdb\x1f\xc3\xf2\xce'\xa0t\x06\x12\x81\"\xa0\xdbc\xe8\x19U\x1an\x95Lp9@d\xe1l\x01S\f`\x8aU\x14\xa3U\x183\x805C\x883\xc12\xd7\x02\xec d\x01k \xa6\x803\x05l\xe1\xfft\xa3\x11\xccp\b;\x1c\xc2\x0e\a\xb0\xc3\x01\xa4\x18B\xe5\x06\xbak\xe0\xdc\b\xd6\x19X\xb1!\xe9\xc8\x02A^im\x01kG~,[lI\u00b3\xf6\xc7j\u0242\u0331\u64ce\xb2I\n\x12P\xcd\xc3[\xea\xfcC\xb3\x92\x95\xa4:\xcf\"7\x14\x9c\x1f\x11}\xd1\xdb\u87e4Q\x9c1\xc2\xfe\xe3m\x87g5\x90Q-\x10[Z\x06\x98\x9a \xe6d<r\"\xd5\xd3HF>s\x93+\x93\xb3fFv(\xa8=\r\xe2\x04&4b\xb3\u0408\xf6z?\a8\xaf\xf9v\xf1\xcb\xf8?\xad\x11\xd80\u0623\x9c\x94\xdav\xa4^\xee)\xb1\x1f\xfen\x1d0\x18Y<\xb9{\x88\x1dO\xf61\x18\xda\xd0@\x0e\x8d\xf9\xa8|R\x04V\x9e6\x8b7A\xd2\xd0\rIr4\t\x96\xe7\xcc\xc1\xc6!\x86\x91\x10\xcaD\xa1\x9dw\u0782\xc7n\xfa\x1a\xec\xa8\xef]\x13\x9d\xc0Yo\xeflM\x81<\xd7\xc8\xf3|\xfe\xf4\xd3O\xbda_\xb9\x8e'`>YI\xe1E\xab\x87\x1f~\xf8]\xd3\xd3\xd3 f\xdf\xedOB\x1eH+\xac\xcc\xef\xc4\xf2\xce\xed\xbe\x92\x0e\x95\x17A\xd0\xe9)`JyYXc\xea\xaf\xdc\x10\x9c\x83u\x06\xce\x198[\xc0Y\x03gM\x00\xd8 gd$\xf3\xde!\x8a.\x06_XS\xe6\x8c:k\xe1\x8c\x05\x91 \x9fV\xc8z\xbe\x8a-\x9d\xf4\xa2\xe1W\xb0*ub\xe1\u0107N\x1bW\xc0\xb1\v`\xc0a|\xbf\x81\xadR\x85mV\xb4\n\xd5hri\xce*\xa5\xb1\xcde\xd3NU\u0464\n\xa5\xea\xa2\rZkWe\x96D\xf5M)\xcf\xe9\x97\x1cxJ\xe4\x8f;\x9f\x8c\xa9E\x9a\x8e\x84)\xf5\x92\xfa\x86k\xaf\xba\xa9\xf4\xf3!\xb2-\xe8\xb7]\xf4-d@\xf5\x18\x1cl\x82\xe3\xd4e\xf0g\xf3TMi-+\xc1\x06B\x90\xb1\x0f\x98P\x8d\x87]\xfe3\xa1\x87\x00`4\xb2\xd8=?\xc2\xee\xf9\x01\x8a\xc2\xf8p\x8d\x92\xd3\xf6@L*\x84N$\xc0]?\x85T\xb6\xb9\x84DvJ\x95\x92\x89\x98\xfdO\t@J\xa3?\xff$\x1e\xb9\xe9+Xy\xf2q\xb0\xca\xfc\t\xc1\x85\xfe\x8e\xb30\xa6\xc0\xf4\xf44\x0e;\xec\xb0]\x97]\xf6\x8aoN\xc0|\xb2\xf6\xc9U\x14\u0177\x0e\xdbxX\xe0\x1d\x15\xca(s\x02\x88\x14\xcc`\x80\x95\xf9\x9d0\xa3Q\xc0Z_\x8du\x14\u041bQp\xb9\x97\f6r\x18*\u03d6\b\x80%\xd8\xfa\xafJ\x96X\xaf\n\u02f3vR\xc6E\xa0\xf6Gg\x01\xe7\x84\u03acB7\x8c\xbb;F\xd9\x18\x15\b\x1c\v\xac\n\x80$\x0eV\f\x80\x02\x8a\x02\x9f\x9dq\xbdQW\x17\x0f\x06k\x01jt\x12\xa9\xe5\xe2I\xa3\xd0B\xdaE\x8f*_t\xf6\x80.M\fo\x94\xcd\xd1+>\x86P\xd0\x14\x97\x89GT\x93\x82\xd4E\xd6\u037e/5.\xf0Z*O\xec\xefR\xe0\xcecfh\xfa:\xa3\xca\x1a\x05|\xd5\xee\u008d\xaaYU\xf3)\x8f\xef\x8b\rU\xb9\xb5\xc1'\xc5\xfa\u05eb\x9e(\xd5|\xd5\xc2\xf0\x8f\x8b\x01I\x82\xfe\xd0b\xe7\xee\x02\xf3\v#8k\xc1\\\x8d\u06f3\xf6\xcd\xe1\xb2\xe3*\xf5\xbc\xd9J8%\x89\u02c3\x84\x8a\u07a7/)\xed7\x030\u0574\xee\xc4\n\xfd\xdd\u06f1\xf8\xe8}\x10k\x03\xd0Gu\x90\x83\xb5\x06\xa6(033\x83\xe3\x8e;\xf6>\"z\x12\x00>\xf9\xc9\u007f\x9c\x80\xf9d\xed\x1b\xeb\xee\xbb\xef\x04\x00l9\xe9\xa4\xe5\u00cf8\xbc\xac\u0309\t\x04\xf6U\"\xfcD\xdd\xf2\xae\x1d\x18,-&\x86C\xfeb\xec*`jF\x01yt\xe6\xab<?\xa2b\xa0\xf4\u00e6\x84\x91 \xef\x8bR\x02y\xb8@\tU2M\xac\xb0\xa5q4'\x110\t\xb2)Fg\x9a\xd1\xc9}x\xafP\xb0\xe8%\t_\u0443\u011f\x00\x98\x05\x10\x03a\x1b\xae\x82\xc6\xd0\xd1\xd8\xe8z;\x10U\x99\xa6\x8dvc\xfc\xb9h\xde\x15\xa3\xdd\xc2\xc8x\xaa3O\xd17\x86\\\x18'>\xa6o\xba\nBn\xe1e\xb0v\nP\xb2WP\xeb\xccO\xf5=E !P\x18\x8d'\x178\xf51J\u0217\xb5\xa6\xcb0]\x8eA;\xfe\xfdp\x80\xb5a\u042c\x11\xfc \xe2*Z\x05\rN[\x00q\x95n\xdc9\xc1j\xdfb\xe7\xae\x02\x8b+\x05\x00\a\u05be\xe1\xcd\x01\x80)\x18a\x89s\x15\x98\xa7\x13f\xd13\x87\xabH=\x0e\x95\xb8\xd2\x04\x9d{\xa9\"\x10\xc2:\x90\xc8E\x89`G\x03\x98a?)*$4a\r\x8ab\x88\xa8\xf0\u06bcy\xf3W\xe2\xcf\x1c}\xf4\xd1\x130\x9f\xac}c\x1dw\x9c\x97(\x9eq\u05bfy\xa8?\x18}+\xeftA\xac\xaa\xd4rT\x8d\xa3\x95\x85y\fVV\xc2\xff\a\b\vWc'gt\xa7\x19\xc4\xf0\xa6]e\xe8n\xc2\xf1\x12jy\x90e&X);\b\x00 \x15\x18\x94\x19\x8b\xb1rNS_\xc4\x1f\xbfy\x8a\xa1;\x04\x1d\x8e\xe3.1\xfdB\xd4\xc0\x97G\xec\b\x9e\x1e\xe0\xd3@\aB\xaa\x85oC\xf1*\xe0Y\x1az\xe9f\xba{\xa9\r\x0f\xf7\xe7\x0f\x02T\x02e\x19\x87GU\x15lC\x83\x8f;>v\r-@\xdc\x04ri\x99\x19\xaa\x019\xd5O;)xQ!\xa0U\vg=\xfd\x9d\bw\x12\x1e\xdd\u007f\x99\x0ec\xd0e\xac\x1a\x81)B\x90w\xe0\u015dqej\x0f\xa5\xe1\x13\xc9\xccB\xd4us\xa05\xac\x93\xe05\xee?\x13\xab}\x8b\x9d\v\x05\x96\x87\x16\xc4\x02V\xe2#\xf1\xcaCb\xf2\x9eK#\xfd\x87jm\x87\xca)\x80\t\xa4\x18:S\xc8r\r\x1d\xfcW\x1cE\xe9hu2c\xa51Z^\x84\u9bc2X%/\x9a\x835~~\"\x16'\x87\x1ez\xc8\xee\xf8:\x9fz\xea\xe9O\xfb5<Q\xb3LVmm\xfe\xb9\xc3W\x0e\u07b8iG\xa7\xd3\xf3i+H\xfdH\xfc\xc51Z]\xc1h8,\xb5\x85\x92\x14\xd4 \x87\xbcC\xa0\x19F\u007f\xb7\xf7\x97\xa6\x94d\x0e\x90\xcee\xc3-H\xc2\xe2\xd0OHN\xe7`mZ\x8dL\xfaQs!\xaa\x0f\u02e4\u0702\x06\xa8KP}\x81\x90\x85%\x01\a~\x87\xc4_\xbcB\x14\xb4\xeb\f\xb0@THV\xa2\n\u009bp9V\xf3\x8e\xd1\x04\x95d\xb2\xa6\U0010ea99)\x05\xacz\xa4d\x10\x14\x8b\al\xa0\x94\xf4\xc5M\xcdi\xff\x18\xb3\x9c\xa0\xa6\xb5\x0f\x8a\x96=m+U\x00H\xfaR4\a6\xd3\xef\x8d%\r\x15\x0en`1\n\xde%\x14\xbd\x14\xd2<M\x006#\x8c\xa6\x18\x05\x03\xcb\xcb\x06d\x1cf\xba\nq,\x81bt \xca~h\x99\xe6\xe3l\u0578TD\xe5\xb0\x12B\x0fAD0\x18Z\xcc/\x1b\f\v_\x89\x97\xea\x11\x9bn\xf0\xae|l\xc4\xfe\x9f\xd4\xf0\xe3\xe1Pi\xc7\x0e\x06\xeb0\x05J\xd1&\x82|\xcf6\u0775\xa2\xd6\\,V\xb6=\x84\xe1\xf2<tw\xaa\fhv\xc1\n7\xfe\xbb\xd7\xebbyy\xe5\xdf\x02\xf8\u043er\xedN\xc0|\xb2\x9ak\xe3\x99\xe7\xfc\xc2\xf3n\xb8\xe1+\x18\xec^\x00)\xa9\x1d\xca\t\xf0\xc7\xd0b\x94\x9cj\xa5\xacV$\\h\xd9\x14C\x8c\xc2p\xd1\xc0\x19_\x98\nU\xb3\x99\x02\x0f\x12\xc2\xe4A\x98\xbd\u04e2D\xb3\xc5p\x81ID\xbaRYR\xcf\u03d4\xb2J\x0e\ua64e\x80r\x01\x0f,\n\xe3e\x82\x04\x80\x8c\x1fqt\x01y\x9c\x05\x9c\xb0\xd7);\v\x04\xa3\xa5\xb4\xc8\x1b\xeb3\"\xd5i7\"\xdfd<yG\x82|\x8e\u00b4%\x86\x9e\xdea\x0e\x15\xb9\x15\x90P\xd5\xf4e\xae\xd4$]\x86\xebr\xb2\xc1\xb4\xa5%U\xe1\xc4\xcdnb\x9b\x86\x9d\x1a*\x15q\x80\x85\xa0\x00`\x00X+!1\u067fY\x92\xb8?\x1aM\x18t\t\x05\t\xa4\xf02\xc3\xdd#\x87~\xdfzj\x8b\x83\xb5C\xf0\n7\xc6\xc1F\xee\\\xa4\xb4e\x88\xaf\x9bV\xc0TW!\xcb\x18B\x04c\x1dV\x87\x0e\x85\x15\xef`\tW\xa3i<\x90W\x80\x1d\x9fY\xe9?\x1e\x92\xaa#`\xab`M\x10?\x1a*I\x1ct`8\xe7B#\x9a\u02a2Du:X\xd9\xf18\xe6\x1f\xba\xa7\xe6\xea(\xe2\x81\u071aQ\xad\xfd\xb0c\xc7\xceC\xf7\xa5\vw\x02\xe6\x93\xd5\\\x1b6\x1cr\xd8\xcfM\u036e\u00ee]\xbb\xa1\xda2\x1a\x9d\xf5\x04)b~f:\x89R\x01A6\xad\x00\x03\x8cV-\xac\xf5\u0382`\x02\xd9\x18\xca\xeb]\x0f\x89\x01\xe1\xd0\u0434\xfeZq\x89\x93_\xd9\x04%jL\xbe \t\x85\x0e\xa7\b\x06\xa8G\xc8\xfa\x04[8\x18\xe7*P3\x14\x1a\xae\xec+G\xb2Pb!VA\x94\xf3\xda\xeeXR\v%\xc0\x910\xe2\xd2R\xea\n\x1aTTR\xf5\x89\xa7\b\xa4\xab +6\u8ba5\xa2XJ<\xa1\x10\x8b\u7a56aF\xe8\xaad\x8a\x9ch\xccy2Up\xb4\r\x06\xc5\x1d\x86\x1a^\ua945\x8e\xf8\xb8\xb4\xc5\xc2\xc1u\t\xd3C\x82\xf2\u0495\xe0sB\xb0\x00\n%\x18d\x84B\x04(\xbc:%\u01ad\xad\x1a\xa0\xdf\x0f\xef\xa1\x04\x9f\x13\x97l\xb6\x89\xc1\x16Q\x95\x02e\x1d0\xb2\x02\xa5<%\xe7\xc2\v\xad\xf2 \x89uRN\bc\xcc\xf5>(e\x92\xa0\x13\nt\x8ab\nn\x90\u0546\x16\xdd\x12\xe3g\u060a\xef\xa1P\xa2,\x15'Py\x17\x8b\x8f?\x80\xc5G\xef\x87\xeet\xcaW\xcb9\vS\f\x83\x84\xd6?\x15c\f\xb2,\xfb\xf1\x04\xcc'k_^J\xb2\x1e\xf4\xd4\\\xf0Z\xf1\x17%S\f\x8c@p\x94c8i\xda\xc4V\xc0\xc6 \x9fW9\xa3\x00\x11\x8c\xfa^\xe2\xc6\xde\xc9\xc8W\xa4H\x94-\x11rC\x05fC,\x1a\xc7H\x9c\xb8W\u0109\xcbF\x8ee\x82 \x90\fP=B\xc7\x10\xdcH\xbc\xe2F\x82\x1b^\x11\xa7\x0e\t\x02\vk\rP\x10lt\xe3\x8b<)5\xaba\x97T\xb8\xf5\xd9~\u007f\"\xa9\xa8\x89V+\x98N\xb0\xf6-<\xdd\xe38\x86\x02KH&\"HHj\xb2\x9a\xd1\xe9*d\xaa\xf1\x1c\xc7\xfeY\xa5\xdeT\xed\aJz\vi\xd6k\xe5\xd3\x02\x01\x8a\xc2ai\xd5`q\xd5\xfaA\x9c.\xc1A\xa13p`\xeb\xfb\rF\x01\x85\x16\x8c2\xf2\xcd\xd88^\x1fv\xb5\xb8\xb9\t\x04b\x1a\x94\x17*0\xadK#C\x89\x1c6'\x87\xca\x10\x8b\x94o\x14W\x95\xb8\x94\xf7S}\xb4\xd2\xcd\x01e3\x99\x88\xa1\u0201(\x9c|\xe2\xe7\x96c\x8f&\xc8B\x9d\x8f\x92\xab\x1c\xd4}\xf3\x95\x15\u00cd\x06\xd8\xfd\xc0\x9d\x18,\xecB\x16\f\xe7\x9c\bL1\x825E\xf9\xd2+\xa5h\u00c6\r\u0634\u9c3f\u0717.\xdcI\x03t\xb2\x00\x00\x8f\xce\xfb\xa0\x8a!\xe0\n\x95Cwz(5\xbb\x9cp\xc2\"P\x9d\x1eDe(\x8c\r\x9ab\x8c!\x98\xa7?\x04\xdc!\xe8\x1eC+\u007f\xaeO\x8d\x8d\xa8\x9cLta`\xc8+\x14\x9c\xf3\x15\xb5\xb1\x16\xc6\x1aX\xe3\x1bm\xb1\x11\uab03\x18\xaf?/\xbf\x1f\xa5\x8e6<\x98\x1eCu\t\xb9\xf6r;\u07d8\x8b\x9e\x1fA\x12I^\u007fn\x8d\x81\x1d\x8d<'Z\xb3i$\xa4\u0588\xd1\xcc*V\xa0\xf1\xc9:\x91\x96\x89\xc3\xe6\x16\xe9\xf3:\x05\x04\xcb>\x0e-R$\xc2\xc1\xf5\x11\xde,J\xcf0f\xa78\xf8VI\xbd\xa1\x97\x10+^bQM\xf5P\xc9Q\a\xba*\xe9@F\xc1\x87\xb3\x82\xe5\x15\x83\xed\xbbGxr~\x84A\xdfxgB+X\xe9\x10\xe6g\x15\x16f\x18\xcb=\xc6j\a\xe8\xe7\x04\x13\x8f\x1aQ1b\x05b\x91\f\v\xc5\x10\x90\xcay\x90\x12\xba\xa7\xd4}\x97\xaed\x95N\\\xe5\f\x95\xb1\xf7\x92WTy\xe2\xc4Ppjf\xc4&\xee\x9a\xd1KE\a5\x14\xa5\xf9\x11\x1e\xe0]\x98\x9aR\xe1\xe4`k\x01\u05b1\xa0\x10\xe8N\x17\u02cf=\x80\x9dw\xdc\f\x8aY\xb8\x10?\xec6\x1a\u052a\xf2\x03\x0e8\x00'\x9d\xb4\xe5;\xaf~\xf5\xbf\xfd2\x00|\xf4\xa3\x1f\xa1}\xe1\x1a\x9eT\xe6\x93\x05\x00\xd8=\xf4\u0235\x00\x18C\x1aJg\xb59\xf5*\xf4\u01c2\xbb\xb3(\xa8\x8b\xa20\xc8\x1a\xc1\x03\xa9C\xac\x8b\x00\x94\x937\xb4\n\xea\x87\xf2\x8c\v\x01\xbb\xc8!W\xbc\xafq\xc0\xd0z\x10\u0395 S\xec+kix\x17Z4-I\x93O\xb6\x1fUW#\x87\xac\xf0\xaa\x898\xaa\x0f\x17=Fbc\xcd\xc2Y\x02F#@\x00\x95\xe5\xe5 \t\t\xb5\xba\xff\x95`\xbf\xb6J\xb0^F3\xbc\x9e]Y\xb8\"!=\x92\xecN\xc7\x00:\x8c\xee\xb4*#\xdd(\x95\xcd5n[\x12\xa9f\xa4\x87\xbc\x9dkh\x16\xbb\n\x83\xe1\x04##X\\5X^\xb1\x18\x8dl\t\xc2\x12\x1f_\xdc\x178\xfe\x9b*\xf0\x1e\ubf3a\xc6\xf7\xa4\xb2\u0265\x1a\xabS*JJ O\xaa\xf0*\x14\xa3\xf1\xbc\x82&<Rg\xf1=#\xe6d\xca\u022b`\xc8\x01J\x04~~\x88\u02ea\xbctc\x88\x9f\xc7t\x82@\xa4\xf2M\xcfr\xb8\xa2\xc0\x8e\xbbo\xc1\xf2\xceGAJ\xfb\xcd\xdb\x1a\x14\xa3\x01\x9c-jo\xe7\xf1\xc7\x1f\x87\xe3\x8f?\xfe\x0f\xe2\xbf_\xff\xfa_\x97}\xe1\x1a\x9eT\xe6\x93\x05\x008\xe9\x909\x01\x80\xc5\x02\x87\xac\x16\u05bb\x1c&\\e\xea\xe67}\xe0\xc1\x98=`\x0e\x19,\xfc\xc0\xa0\x8cM\x19\xa6L\x05g\xec\xdd\x15\x93\x19\x13?\xfa\xed\xbc$\x90\xaba!\x02\xa1p\xc0ja\xe1D\xbc\xcb P\x1b\xa7\xafM2\xa6G\xf2x\xec\x0f\x15\x1du\xfc`\x8b\n\x16\xb4%\xe7\xea\x82Wv\x1c,r\x02\t\x13\xa5\xa5\x85@\xac\xd0\x13\x80\xaa\xf5^\x81Z\xd5Xk,\xb6`\xb9 \x04EG\xdb\x00[\xe9\xa2)\x9c\x1a\x1c\x00\x97\x05\xcfsS9GJS\xb7m-\x9c\xf5\xd5t)\xd1s\xbe\x81\xe9\xa5\u05c2\xe1\xd0ai\xb1\xc0\xd2B\x81\u0565\x02\xf3\xf3#<\xb1\xad\x8f]\xbbF\x18\rmU\xd1\xd7N\x15\xb1\xd2\x0fQl\xc6A\x8c++\u007f\x89\xa7\xaa8\xa9\x19\x95LI\xf0\x06q\xeap\x15@[3(S\xe0\x8e\x86\xca\x158S\xde.\x82\x93\xbe\x04\xd59\xff\xb2\xefL\xd5f@\x9a\xbd\\3\xe3\u048a\x81\xe0\u9c4c\t\x8a\xbc\xdd0\az\xceIU\xad\xdb`\x0f \xa9\x1ds\xb8/\x95eX}\xf2\tl\xbf\xe3\ap\xd6\xfaq0\x11\x98b\b3\x1cT\x9f\x05\x00sss\u0632e\u02d7\xdf\xf1\x8e\u007f\xff\x15\x00\xb8\xfa\uafe5}\xe5\x1a\x9eT\xe6\x93U\x81\xce5o\xd6w\x14\xb8\xa0\xdf_\xc1\xea\xe2n?\x05\x1a\xab\xbf0m\xa7\xf3\x1e\xd6\x1f\xba\t\xb33S(V\x17\x92z\x80\x12n\x93\xc6\f\x94J3\xab\x91\xe7\xaf-\x00\x16\xef\xa0'\xd6gF\x92\xf8\xe6\u0520\xb0`\x89\xa3\xdfA\xd9\x10\xaap\"\x19\xdb5$\xb8;\x96\xd1a.\xc9\x15\xed2\xa8O`\xeb\xedt]\u0d8dq\u022c@9\a\x10{G=\xb1p\x96\x01\xb6 \x1b\xf8[\xa8\xa0\xac\xa9\xaah\x91\u0532@jsBi\xa5]\x9d\x16\xa8\fM\xb6\x10\x14Q\xb7\xe7*\xab\x00G\x80Q\x02\xeb\x04f\xe4\xfc\xeb\x03\x86\x0e\xbc\xb9+cM\xbd\xd4/N:FS\xb0\xa8\xe9\x87\x00\u00e1\xc3\xc2r\x81\xc1\xc0y\x8d6\x80Q\xe1S\xe9Yy~:r\xdd\xe5\xfb\x14\xabo\x97\x94\xdb\x12o]\xaa\xed<\xf5\x1dK\xacl\xab\xc9\u02eaI\u0351\x0e\xa1h\x18V\xff\xfd\xaa\xf9\x9b\xf4\"\xa8R3\tG\x99d\x90\r\x86\u03e0\xd8\U0001e2c0\xe1\x87\xc6R\x99\xb9$\x9b0\xc37zm\x943\x82\xe0\xca\u03ea\x80\x94\x02\xc4a\xf9\x89\x870\u063d\xdd\u007f\x9f\t\xd6\x14(\x86}\xdfSI\xda\x14\xa7\x9f~\x1a\xce<\xf3\xcc?'\xa2!\x00\xbc\xeeuo\x90}\xe5\xfa\x9dT\xe6\x93U\xae\xdbN\xfe\x8d\r\xfd\x81}\xc1\x13\x8f>\x8c\x9d\x8f<\bV\xaa\xac\u0108\x00g\v\xcc\x1e|\x18\xd6o\xfc9\xc0\x19T\u04cf\xa8\x94\vc\x9d\xbf0,B\xe2\xa9\x16\xf2a\xd1\xcez\xa7\x0f\x82\x83\x83\xf5^\xe7#\x83bP\xc0\x0e-z$\u0202\u06d2\x18T\u0561\x95\xd2B \xe9\xc0\u008a\x84\x01\x14\t\xb4\xb8\xaf8U\xc7Ws*\x84/\u0107U\x14\x82\xd1\xc0\x86\xa9R\xcf\u04fbh\xa6dm\xe0\x9d]\b\x9c\x96\x92r(\x9b|\x0ep&\x04)X?0\xe3\x8aP\xc9Z\x01Y\a\x8a\xa3\xed\x85\xff?\a\xc1\xd0\tL\x11\x86glh\u0205\xaf\x11\x01\u00d1\xc3\xea\x8a\xc5j\xdfb0\xb0\xe8\x0f\xfc\u07d7W\r\x16\x96\r\x96V\f\x86#\ak|pC\u007f\xe00\xbf\xe0\xc7\xde\x17\x17\v\xec\xda=\u0136\x9d}\xcc/\x8e0\x18\x85\xdb\xe8[X#\xd5LVt\u038a\xbd\x8exR\xb1\xc90VR\xb1\x8f\xd9\xc1DM6\x85\xca[q\u5552\xf9\x04#\xd5ap\xaeJ\xc30j&8\xa5\xf9$TQ/\xacB\xb0v\xbc\xad\xf0E\x89\xe3B\x9c\xfcd\x12d\x1c>'\xe1q\u0692\xc2\x0f\x15:3\x1c\bB\x89q\\\xe2\xb5C\xe4C\x9b\x8b\x95\x05 \x19\xdb/\x86\xab0\u5d27\xff\xe1\r\x1b6\xe0\x94S\xb6\xfe\xc3\x15W\xbc\xf1\x06\x00\xf8\x9b\xbf\xf9k\u0697\xae\xdfIe>Y\xf8\xf1\xbd\xf7\xe1\xa4go\xc6\t[\x9e;\xf5\xd5;\x1f;\xf9\x87\u07f8\x01K\xf3\xbb\u041b\x9e\t@\xee\xa7\xe4\x9c1\x98y\xd6&\xcc\x1c\xbc1|\u0429\x96r\x1f\xf4n\x151\x13)W\xeb`\x04@\x16\xe4w\u05abc\x14\xfb\xc6\x15\xe9\xc0\x8f:\a%\xc0\xb4\x06:\xca\x03\x8e\x8bMX\xc4\x01%*/0W\xfaySP(\x04\xa1DPL0\x93o\n\xc6)?[m>\xce:\xf4\x97-h\xca!\xcb\x18D\xde\x1f\x06\xca\xfbs\b\xf9\xd8;(\x8b(\x87p\x0e\x15\x9d\x13\x1eC\xc3\b\xb7\xa4\xfeK\xccw(\u04da\n#X\xb5\xbeJ\xcc\xc3\tB\xc2\xc4g\xa1\x01\x03\a3\x12\xac\x8c\b\xc5\u040f\x9cKR\x95\x1b'\xc8\x14a\xaa\xab\xa0\xd97\xf7V\xfa\x06\xab}\x13\xaa2\x82\xb1\xe15\xabq\xedR6n)\xf5+\x97\xd4i\x91\u01a8\xa1\xb4/PU\xbc\xd4H\xf2\xa9\xf2[\x91\x84;\x8c\xadTb\x99~+n\b\xd1@\x8b\t\u03b0\xaf\xa9-\xfb\r\xbcv\x18\xf3\x958\x91@IP\xef\xc4SS\xb0\xe8\x15\x00:X\x05\x1b'\xb0\xb1!\x90\x98l\xc5S\x8dw_\xd4\xe8\xce\xccy\x96\xc9\x1aXk1\x1a\xf6\u02e6\xa7\b\x9c\u059aO=\xf5y\x0f\\y\xe5\x15\xff7\x11\x15\xef~\xf7\xbb\xb2+\xaf\xbc\xaa\x98\x80\xf9d\xed\x13\xeb\xd1G\x1f\xc6\xf7\xbf\u007f#Nz\xf6f\x00\xc0*0\xfd\xc5O\xfcw|\xff+_\xf4MIq\xa5`W\x9c\x03\xeb\x1c3\x87\x1e\r5\xb5\x0e\xa6X\xf5\x95.\xb3?\xaa\x82@*\x83\x90*\xe9\x8e\xc28X\xe7+(f@2\a\xd1\x02\t\xc1\x13\xba\xa3||\x99V\x81&A\xa0W\xa2\x02\xa5\xba\xea\xa5\xd4LK\xe2\xc2\xe8\xabp0\u01d3|#\xc5GJn\xdc)@\x8a\xe8#\x13\xaa\xf3U\x83\xfe\x12\x83f3?5\xc8\x02h\x80\x11\x142\xe45\xe9\xe5m\x05`)\u00c4\xb9\n\xa2\xaf5\xfc\xaa\xb6\x00\xacu\x81\x86\x16,\xf7-V\xad\x03\xeb\u0434c\x81c`\xa4\b#\x0eC;V`D`FR#\x92)\xf8\xc7\x14\x00\x86}\vf\x82\xb5\x02\x13\xaai\x8bJy\xc3A@\xed\x15-R\u6596`\x1cOP\"\xed\xa3\xa2\x8d\xac\xcfR\xcd\x14\xab\xe8\x98e\x1a\xfdf\x12\x95J\xbd\xf1\xd9\xcc\fM\xfe+\x00\xb7\x8fi\xe3\xa4Z'\x90\xadxwo\xf8\xe6\xca\x17=>\xe4\xe8\x89n\x1d\xe0\x1cC3\x82\xaa(<\u007f\xf8M\xb3\x90\xa81\x0f\x1e-I\xaf\x85\xc2\xedw\xbb=(\x02\x06K\xf30E\x81b\u0507\rMO\xa5\x94Xk\xe9\xb4\xd3N\u0165\x97\xbe\xf4\x1f\x8f?~\u02dd\x00\xf0\xf6\xb7\xbf\u077c\xef}\xef\xc7\x04\xcc'k\x9fXw\xdf}\x17^\xf6\xb2_\x8a\xbcs\xefw\xff\xe0\x0f\xfe\xe8S\x1f\xbb\x1a;\x9fx\x04\xdd\xee4\x9cu\xa5\xc1\x96\x19\r0u\u0421\x98;j\v\x06#\v\xed\x1c\xf2L\xc3\x19\x83\x1dw\u07c6'\xee\xba\x15B\x8cl\xfa\x00p\xd6\x05\xe7=@\xf5\xa0\xf2.\xa6f\xa6\u041b\x9a\x02e30N\x83\x95F\xa65\xa8\x97Au\xbd\x91\x94\xb0@LQU\xf6\xc2U\x05(\x95\x9a\xa2\xe4Y\xa3a\x1e\x04\xcc\x0eJ\x92\xb0\x8b8`\x14mX\x15 \xdaW\xf2\xb6\xec\x82\xf91\xf6\xc1R\x01\xce\b\xdd\\{<J\a\r\x85R\x19O\xbd\x0f@\xf5\xc0\xb6\xd8\x0f\x95\xe4\x90\x12' G\x85\xc3J\xdfb\xb5\x1fxk\x06\xac\xf6)\xf1\x8e\bV\xf9\x1eBt\x87\xa4\xc4G7N\xcdzJ\xc4\xd7\xdc\u01a1\xee\xec\x18'o\xe3\x10U[\xa8(Qb\\F\xe3\x1e\xea\xad\xddk*\xc3\x1b*Y`RE\x03\xa9\x16\xb0\u04b4S\xfdu\x92\xf4wc3\x93\x13\x85Kz\x14\x88\xe1\xde\xd5\xee\x01\"\x15n\xc3\xc5t@\xa8\xe8\tI\x95\xd7\x01\x13\xc0Ba\u0407`d\u071f,nbD\x04\xa5\x14\xa6\xa6\xa71\u06bd\r\xf7}\xeb\v\x18\xae,\x95\xde\xf7~\xa2\x95`\xad5G\x1f}Tv\xfa\xe9\xa7\xdf\U00096dfc\xf5\xfd\xff\xee\u07fd\r_\xfa\xd2\xf5\xbca\u00f3\u073ev=O\xc0\xfc\x19\xba\xe6\xe7wa\u077a\x03\xcb\u007f\xff\xe1\x1f\xfe\xc1\xef}\xfes\x9f}\xf1C\xf7\xdf\v8'\xce\x19\xf2>\xe3\x1a.\x94\xb2\a>\xfb\xe71w\xe4\xf1\x18\fG`\x00\x86\x18\xa3\x95%\xdc\xf7\xc3\xef\xe1\x81\xef]\x0f;\x1a@uz`\xa5\xc1:\x87\xd29T\x96C\xe5\x1ddy\a\u0719\x02\xeb.\x14w\xa0\x14\xa3;5\x8d\x83\x8f<\x06S\a\x1d\x84\xa9u\ab\xf6Y\x9b\xc0*\x83s~\xf8\xc3k\x83\xebcIH\xc0:\x0e\x15Q\xe0\xb4]\x90\x1aV\xc7\xe8\xf0\xe3\x04\f\x01\x8c\"\xd7\x1e\xa4n\x10A\xd1\x17,,\x10d\x16\xe8u3Tv\xb2\x94\x1a\xf0y^\xb9\xdcT\xa8\x1a)\x97d\x80'}\x88V0\x18X\xac\x0e,V\xfb\x06\xc3\xc2\x05\xef\x9a \xcfVH@U\xca\u01a3$I=\xf5\xb1\xdb\x16\x8a\"\xfdy\xa97.\xd3a\x9dd\x8c\x145\xa3\x17Jm\x1a\xc2Tn\xec_\x06\xb9h\t\xe2T}\x0f\x8d\xe9Ij\xd9\x10R)\"S\xa5\vo\xfen\x93\xdbI\xbd\xc8%U\u0144!3\xcd@\x0e?\xbe/\xce\xc7\rJ\xe8$\xc7\u03c2\t\x83\xc4q\xaa\x94\x90\xbew\u1e72Bwj\x06\x18\xae\xe2'\xd7\xfd\x1d\x1e\xfd\xc9\xf7a\xc5\xc1\x98Q\b\xe6Vp\xceI\xaf\xd7\u034e;\xeeXw\xc9%\x17\xbf\x9b\x88\u6bff\xfe\xf3|\xe1\x85\x17\xbbo|\xe3kx\xc1\v\u039d\x80\xf9d=\xbd+\x1d\xc1\x17\x91\xb9\xff\xf2_>\xf0\xc1\xab\xaf\xfe\xf8ko\xbe\xf9\x87%\x8a8k\xabtwkp\xe0\xe6\x13q\xc4\xff\xf6R\xe8N\xd7\u02f5\x04\xb0C\x03\xeb4\xa66\x1e\x8b\r\xc7<\x01[\f\xfdE`\n\xd8b\b;\x1a\u0099\x11\xccR\x1f\xabf\x04gF\xb0\xa3\x01l1\x808\x03\xd69\ue7daC65\x8dC\x8f?\x15']r9\xd6\x1fz\x04\u020c\x82\xe1V<27\x89\x00jbQ\xa2\u0328\xa0E\x82\xb6:\x82*;It\U00061135\x9e\xb6\x98\x17\x82\x13\x8d\x19E\xd0\xe29[AU\u0757\x8d3\xa9\xc2\x0flc0&V\xd1\xe2\x04\x8b\xcb\x06\xbb\x17F\x18\x1a\a\xe7\u01bd\xbcSO\x97\x1a\xb2\xb9:pS\x92\xe99\xaec\x97\xba\x87n\x82\x92\xb4\x86\x85oi\xbf[\xfe\x117\x14.i#/1\x8c\x19\xc6u\x1bB*\xa5\x9a\x91\xf6\xa2\x1a\xad\x12\x03\x92c\x88D\u4f9aN\xc2$)\x9b\xdf\xe24\x1c\xf2d\xcb'\xc2\xfe}\xd1\u481c\x9f\xd8u\x89\xad\xb2\n\xdd\v#\xfe\xcb5\xf9\u007f\xaa\x14,L\nyo\x1an\xb0\x8c\x9f|\xe1\xefq\xdfw\xbf\xe4\xfb.\xc5\b\x90\xe0a.\"D\xa0\x13N8\x01/x\xc1\v\xfe\xf2%/\xb9\xf4\xf3\x00p\xf1\xc5/v\x1f\xff\xf8\xc7\xf69 \x9f\x80\xf93\x14\xc8/\xbd\xf4\xa5\n\x80\x15\x91\r\xef\u007f\xff\xfb\xae\xf9\xdc\u7bbd\xf0\x96[n\xad\x95\x83Q\x1b\xedL\x81\xe9\x83\x0e\xc5\xe6\v_\x8d\xf5\u01dc\x8cbe9\xd0\b\x04\xe7,Hi\x1cr\xd2\xd9\xd8p\xfc\xa9!\xb1\xd7W\xb4\xce\x14a\xaar\x003XE\xb1\xba\x8cbe\x01\xab\xbb\x1e\xc7\xf2\xf6\a1\xd8\xfd\x04\x9c-\xc0Y\x17*\x9f\x82\xcb7`q\x95\xd1\xe9\x17\xc8U2\xf5\u01c9\xa7GI\xe2F\xf8\x960\xd4S9,R\xe0\xaaG\xc6b8r\xe8\x0f\x9cW\u007f8\xa0\x13=;J\v\x19\x1f\xf8\u03240\xb2\n\xf3K\x0e#kq\x00\x1cz\x04\xe8tz\xb2\u00b32->r\xe5\xde\x0124e\x9d`a\xa9\xc0\xce\xf9\x11\x8cI%\x84\x187xI\xfe#\x86\x05c\xac9\xd9tkL\x01\xbce\x8b\x8b\xc8\xc8\xd5w\xa9\xa1\x1cI\xff,\xe9\x91D\xcf]\x01\xb8\x00\xcd,\xd0D\x96\x12y\xfc\x12\xf4\x03\x80\x97Fb\xb5\xe7<Nw\xa4\x93\xa2Mn=}\xec\x04\x82V\x04\xc5\xca+\x84b\xa5MH\x147\xfe3`\\\x90\x9f&\x8f\x9f\x13Y)\t\x90\xe7]\x14\vO\xe2\x8e\xcf_\x8d\xfb\xbe\xfdE8\xeb\xa3\xe0\u011a\xca\n\x00\xa0#\x8f\xfc9\x9cw\xdey\xdf\u007f\xf7\xbb\xdf\xfbV\x00\u0638q#\xfe\xfa\xaf\xff\n\xbf\xf8\x8b/\xdb'\xaf\xed\t\x98?3+r+\"\a\xff\xee\xef\xfe\xce5\xd7]\xf7\xf9\vn\xba\xe9\aHr}|S\x93\x18\xce\x14\xe8\u032d\xc7Q\xe7\xbe\x02\x1b\x9f\xf7\v~\xa0\x82\x01\xa8\x90\xbe\"\x1c\xf8G\x85<\xef\x95\xf7\x91V\x9b\x02\xf2#\xf6\xae2]rE\x81\xfe\xee'P\xac.\xa2;\xb7\x01zj\x16*\xebB\x84\xb0{\xd7*fg4\xa6z:p\xe0M\u08925\xa8\x02\xe9C \xb1\x01\x86\x85\xc5`\xe80\x18Y\x18\xe3\x81=R\x046\xfapH\xd4>\x13D)\xb0V\x10\xad!PX\x19\x12F\xf3\x0e\xbd\x91A\xb7\xe3\xd0\xc9\x14\xb4\x0e\xca\x18\xaa&2]h\x86:\v\x14\xd6z9\x9c\x13\fG\x0eK+\x06\xc6:\x9f'\xd9B%4\xc1\xb9\x1ct\xaaH\xe3\xf2\xb9q\x02\x88\x92\x00\x9c$X\x8b\x14\xc4S~\xba\xd9xL\x8c\xcb\xca\xe9Kj\xfcn\xba\xebPL\xbb\xa7\x8a\x87\xa7j\xac\x1e\xa1\x01Zj\xc8\xc3mH\xc2\x1e\x11\xd5\x19\xa3\x9a+e\x83M\x8a\x8b\u00ddH\xf0\xed\xc9\x19\xd0a\x1a\u05c9\xf2Q\x85\xc4\xc1\x06\x82\xc0`\xaf%\x0f\x12X\xa1\xf43\x18g\x13|\xb9\x9eu{\x18-\xed\xc6\x1d\xd7\x06 \x17\x17r=\x87\xe5\xfb+\x00fgg\xf1\xdc\xe7\xfe\xfc\xb6\xb7\xbd\xed\xadW\xfe\xe9\x9f\xfe\x19\x00\xe0?\xfc\x87\xdf\xdeg\x81|\x02\xe6\u03e0u\xcd5\u007f\x9fR+\a\xbf\xef}\u007f\x10\x80\xfc\xa6\xda%\xc5\xca'\x01\x883\u041d9l\xbe\xf0U8\xfa\xfc_\x02)\r7\x1a\x822\x9f2\xcc\xc6V\xa92\xd6\xc1\x9aA96^\xbb>%\x89\x14\x82\x97\b\xeaN\x17\xb3\x87\x1c\xdd\x18\xfcqp\u01a0\xb0\x84\xa5\x15\u03c1\xf6z\xde\xcat\fD\x02\xc00\x01\xc6x\xe0\x1e\x15\x1eH\x87A\xc3\x1dKiV\b\x8a\b\xcf\xdfZ\xf2&_D\xec7%\xad\x81\\A\xb1\x0e\t\b\x04\xe3\bK\xcb\x05VV\t\x99fd\xc1\x0f[\xc5\xd0\xdf \xf4\xb1\xce\u06fd\x8e\x02\x95\x02\x04\x90\x0f\xd5z\x15\xa9\xd6R\x9d\x02\x15\xe8\xb4\xf0\r\xcdP\x89FQ\x9c\x84f\xa3jDRjnE\xf5)\x9a`f\x85\x10\x9b\u059cl\xad)rP\x81x\xadzO\xe4\x83\u008d\xff\x8f\x15\xb74@\xbf\xad!\xba\xb71\x9b\x90p\xa5D\xa0)\x80\x94\x00V\xa8|n\x12U;>\x85\xd9\x039G\u03df\xf0\xb9\f=\x14'\x9e\x03\xcf;9\x06\xbb\x9e\xc0\x1d\xd7}\x1c\x0f~\xefKeM_\fW}\x97\x84|\x91\xd2\xe9tp\xdey\xe7\xe25\xafy\xcd;\x8e<\xf2\xa8[\x01\xe0w~\xe7\xbd\xf4\u05b7\xfe\xef\xb2/_\xe3\x130\u007f\x06\xac[n\xb9\x19\xd7^{m\t'\u007f\xfc\xc7\u007f\xf4;_\xfc\xe2\xf5\x17\xdc|\xf3\xcd5 \xf71q\n\xceZ\xa8n\x0f\xc7\\\xfcj\x1c\u007f\xe9\x1b@*\x87\x19\xae\x86\x80g\xf1A\x12\x91\xdaH\x06y\xe0\xc4\xfb\x83K\x92\xba#i\x9b,\xd2\x16\xa6\xfcW\x1c\xc0\x11x\x89\x1a\x93\xaf\xa0\x17\xfb\x0eC\v\xf4:\x82,\xf3#\xf9)7\xed\xac\xa00\x0e\xfd\x81E\u007f\xe8\x82D/\x02\x0e'r\xba`\x9b\u01de\x04\x96\x8c\xe1Dy)\x89\xd6\x10\xc5A\x1aWy~H\xa2\xcb\x1e\x8e\x1c\x86C_\x067)\x13)'$i\\\x14\xd2L\x91\x93:s\xd1`\x90\xbd,\x12\xa8\x95\xb2\x8d\x1c\x8e\x9a\u0084\x12:%275uH\xa3\ua9b1\f\xb9\x06E\x938coU\xd1\xd4\x00\x00 \x00IDAT\xa5\x96\xb9\x14'\xad8\xa1`\xa8QqK\xbd\xc0\xde\xdb$\rS\xbb;{\xb5\xefx\u007ft\x8d`\xc9+1\t\xaa2H#\n\ue4e0\xd0\xecDp\\\f'P.Mi\xa0\xb3\fYg\n\x8b\x0f\u0789\xdb?\xf71<z\u02f7\xfc\xe7\x01@1\xec\x03b\xbd+(D:\x9d\x9c\xce?\xff<\xbc\xf6\xb5\x97\xff\xd1\xcb^\xf6\x8a\xbf\a\x80\xf7\xbe\xf7=t\xd6Yg\u027e~\x9dO\xc0\xfc\x19\xb0\xbe\xf8\xc5\xeb\xf1\x9e\xf7\xfcG\x01\x80\u007f\xfa\xa7\xff\xef\r\u007f\xf6g\x1fx\u04cd7\xde\x18='|a\xc7\xec\xaboc\x90\xf5z\xd8\xf2\xf27\xe2\xe4W\xbf\r\xc49\x86K\v\x95\xec\xaf\x04\x8az\xa3\x91\x9c\xc0\x99p\xe1\tU\xae}\"\xa5\xc3\xe0\xd8\xc5\x1e\xffO\u01fcFU\xeb&\x8e \xb0\x05\xa0\xac\x83\x8ay\x8ep\xe5\x84gQ\b\xac\xf5\u0b78~\xeb\x14\a\x85XW\xe1\xd4J\x83Y\xf9\n,|\xf9\xdeZ](\x1e\x95\x13\x10\x80XB\xaf\x8fj4R\xd9L+u\x92\xb4VvD\xfd9\xa7\xfcu#{\xb2\xfa\x15N\ng\xa9R\x908\xe5\xbe\xeb\xbcu9\xdcEU\x95^\a\xefz\a\xb5\n\xaf\xa8\xd34\x14o\x93\xa9\x1d\xc0\xd1\u061c\x1a\x159\xd7\u0741\xeb'\x92=\xa8 \x83\x190\x14\xfc`\x94\"\x05)\xd8O\xe1\u0081\xc5A\xc4\u008a\xb7B`\x00\x16\x15u\x16K~?\x04D\xa5\xcc3\u02fb\u0419\xc6\xf6;~\x80\xdb?\xfb\xb7\xd8~\xd7\x0f\xc1\u02a7\xa1\x14\xc3\x158k\xbc7\xbbx \xbf\xf8\xe2\x17\xe2\rox\xc3\x1f\xbd\xe4%\x97\xbe\v\x00\xae\xbb\xees\xfc\x92\x97\\\xea~\x16\xae\xf3\t\x98\xef\xe7\xeb\x9e{\xee\u0131\xc7>'\x02\xd1\xcf]u\xd5o\xfc\xee]w\xdd\u0163\xd1(9\xd52\x98\x19\xd6\x14\u0226f\xb0\xf5\x97\u07c2\xe7\xbd\xee\xdf\xc3\xe9\x0eVvn\ai\r\xb1\xa6\xd2A\xd7xP\n\u0578\xb7\"\x8d\xbe\xe1\xa5\xc4\u03c6\xe0]\x17\xc2'B\xb5\x1e\xbd5@R\x1e\xab)N\xc7P\xacJ=\xd8G\x85\x82\xef|Q\xa5E\xce\x00\x9d\xc7n-%\x13\x8a*T\xe7\x1a\xa45\xa0\x14\x88t\u0253\x97\x0f?\x04B\xb7Z\x1f\xc6\xd9\xf0HE\u0101\x95\xb4c\x17\xef3\r\x9b\x93\xf1&\x1e\xa8\x85zH&dk\u07d2jH\xa7tMlJ\xfa\x12\x15\n% \\\x01r\xcd\xf0\xb2\xe2\xcb+\xcd_Y\xd1S\xfaC\t\x8dRo\xa26\x00\xbaI\xfb\xb4\x1f6\xea\x89MkT\xe1\u9a40\xe1\a\xc6r\xf6T\xcb\xc8*82\u07bf'd\x8d\x86\xb8W\x18\x01\n\xe7S\x83@\xc1\xfe\x98*k`!B\xd6\xed\x82l\x81\a\xbeu\x1d\xee\xba\xe1\x1f\xb1\xf0\xf0O\xa1t\x0e\x10\xf9\xc1 3\x02AP\x14\x85LMO\xd1\xc5\x17\xbf\x10o|\xe3\x15\xef\xbf\u4497\xfc\a\x00\xb8\xf5\u059b\xf5s\x9f\xfb<\xf3\xb3r\xadO\xc0|?_\x11\xc8\x01\xe0\x83\x1f\xfc\xafo\xbe\xf1\xc6\x1b\x8f\u0736m[\xedx\u034aa\n\x83\xee\xf4\x1cN\xbf\xfcmx\xdek\xdf\t\u05dd\xc2\xca\xe2\"T\xa7\x9b\\t\xae2\x92J\bQ\x0f\x8c\x14\x9aR\f\xd2\x1a\u0119\xbf<\x03\x90;k \xc6@|\x84{Y\x95\xc7x\xb8\xb1+>\x98g\x11\xd7K\xdd\nP\x13\xb8\xf3\x1dN_\x89+\rRY\xa0U\x94\x0f\x9d\xe6\xa4\u04a6\u0291\xd6S%\xd4Z5\x8a\xf8\tG)\xfd_\xfc\xe9\xc3k\xc5)\x89\xabk\x80T\x1aRD\ud217\x0e\u05d0\xb4P!@5\x0f\x934\x16+\x86\x84\xc6h\x97&\xa5CM\xfe=\x8d\x91\xe3\xea1D.\x1c\xc9@O\xeb\xd0\xd1^\x165\x03\xa3k\xdc~rPI\x8b\x80\x12\xed=\x90k\x06r\xae\xf8z\xa5\x18\xb0\n \xeb\xa78\x01(\x12\x14\x16\x18\x05-y\x15:\xc5a(\u0281\x88\x91O\xf50X\u0609\xfb\xbe\xfci\xdc\xf7\x8d\xcfa\xb0\xb8\v*\xcb\x01\x10\x8c\x19z;\nq(\x8aBz\xdd.\xbd\xf0\xa2\x8bp\xe5\x95W\xfe\xe1%\x97\xbc\xf8\u075e\x9a\xfc\xc1\xcf\x14\x90O\xc0\xfc\x19\xb4D\xe4\xc8_\xfe\xe5W\xbd\xe2\xe1\x87\x1fFEM\xfa)8\xb1\x16Y\xa7\x8b3.\u007f\x1b\u03ba\xf2=pY\a\v\x8bK`\xad\xa1:]\x00\x80eU\x19O\xa5\xf3\x87AM\xe0\x91@\x81\x02\x9dA\xac|\xe5\x1a~\x9e\xadop\x8a5\xc1\xf9\xceU!\xbd\xa0\xe4v\x83\xe1S\n\xe0\xc9\xc6C\xb5\xe0\xe5\x00\x82\xac\x00f(\xed\x81\\\x02\x1fZ\xa3\x02D\xea`\x1a\x13s\x9a\x00\x19\x833b\xa3/\xda\xc1J\x04?)O\x02\xe0\u069eR\x8b\xe1\xack\xb2\u06c1\x1c\xa8\xf3\xdbq\u04e1\x96\xffo%\xa2\x1b\x8d\u0438\v\xd5F\xec\x13\n\x05i\xa5\x9fV\xe1\xa8O\xdf\xd3\xf8Sjm\\\x8a\x8c?4\xda\x03aN\xb5\x83Ju\xa6\xd1\xe4e\xa3\xa9\xab\xa2\x04? &\x86\x8d\xea*r(,P\u0113Yz\xe2\x01\x00\xe7@\xa4\xa0\xf2\x0e\x96\x1e\xbb\x1fw|\xeecx\xf8\xc6/\xc1\x16#\xb0\u03bd\xe3\xa45\xb0\xa3!\b\x0e\xc6Z\xbbn\xdd:u\xfe\xf9\xe7\xe2\xcdo~\xf3\u007f\xbc\xe0\x82\x8b~\x1f\x00>\xfc\xe1\xff\x87\xb6n=\xd5\xfc\xac]\xe3\x130\u07cf\xd7O~r\x1b\xb6l9\x19\x00\xf0\xf1\x8f\u007f\uc94f>\xfa\xe81\xbbv\xed\xae\xae+\xf2\xb2\fq\x82\x93.|9\xcex\xfd;\xa0z\x1d\xac\xce/\x87\xa9z\x02\xe9\xcc\xdb\xd0f\xc6s\xec\xc1\xb1NP)\t(P#>E\xa0\xb2\xc0\xa52b\xcc\x03.g\f\xe8,T\xe5.\x96\xc6U\xa3TB|@\xb4aM\xa5\xe5TQ0\x15~q\x05\x86\xf0\xee}~s\t\u0380$e\x80p\x1a6\r4|\xc8\xd3L\xd14\xe6>z\u03d0\x94\xdar\x92\xca\u0696\xc6\xd0+\x81?\xaeZ\xbe\xf5*=\x89\x9a\xab=\xb7\xc8l$\xf6\xba2N\xba\x97\xda\xf6Fu^r\xe1L\x15\xa5B\x95\u007fw)gL\xbcV\x9ae\xb34\xec\n\xdah\x95t\x83l&\xf7\xd5\x1fI\xf3w\tQh\x1a7\x81\xb4\x1a\x8f\xcb\x06\x0f{\x0e`\x0e\u0170\x81n1\x8e`\x85\u0293\x0f#\x89\x00\x14\a\xad3\xa8N\x0f;\xee\xfc\x01\xee\xf8\xa7\x0f\xe3\xb1\x1f}\xcfG\xc7\xe9\f\x80O`\xb2f\b\x88\x85)\n\u0670\xe1 u\xdai\xa7\xed~\xfd\xeb_\xff\x9f.\xb8\xe0\xa2\xf7\x03\xc0\xaf\xfd\xdaki\xe3\xc6\xc3\xe4g\xf1z\x9f\x80\xf9~\xbc\xb6l9\x19\"BD$\xb7\xdd\xf6\xe33\a\x83!Dd\b\xa0\x13/0g-\x0e:\xfc(<\xffW\u078c\xe9\x03\xd7aq\xf7\nD$\xf0\x92\xc1\xc7C)\x90RP\xd1\x1e\x15i2=UG\xf8hO\x1a*\xeb\x18\xd2SzgST\x97\x04g\xc2\x04\x14\xa9\xbc0\xd3[n\f\xcd4\xea\xbc4\u0772TT8)\x15\x18\xe2Z@\xb1\x95\x17\xa8\xa8\x8d\x84d\xaem$^)!-\xdcx\x12\xa9W\xbb\x91\xe4n\xa4Q\xa9\u05ea\xda \x17\x045~!\xcd\x1aM<N\xa4\xbe\x89\x107\a\x81\xe2\xc0\x0e\x05\xd0G\u0660\xac\x85k\xc88\xeaR\x83\xc3/\xc1\x9b\u05ae\xc4\xeb\xa5:\xc6\x1d\b\xcawQjD\xba\xa2\x10\xbc\x14\x9a\xbc.\f\xfd\x94\x8aN\xf2\xa0.*\x00\xbaQpdC\x11\xe1\xfb-\xe9+\xaf;=h\xad\xf1\xd8\x0f\xbf\x81\x1f}\xe2/\xb1\xeb\u079f\x80\x94\xf2\xfd\x9e\xf0\xe1p\xae\x80\xb3\x05\xac)p\xfc\xf1\xc7\u0469\xa7\x9e\xfa\xf0\xa5\x97^\xfa\xab\x97^z\u0677\x00\u0aab\xae\xa4\x97\xbe\xf4\xa5\xf2\xe2\x17\xbf\x14\x130\x9f\xac}j\xdd{\xef=DD\"\"\xeb^\xf3\x9a_=\xe2\xb1\xc7\x1e\x03\x11\xa9\u0503\x9c\x00\x1c\u007f\u03858\xe4\u06130\x1ax\xcfVW\x83LI\xf0\x8eB\xb03j\xa0[N\xd7ES\xa60\xd2.\xae\xaa\x16K>9!\x97\xe3,G\u0273\n%tJC\u007f!\x82:\x1e\xc8\x18\xef\\\u06948\xa9\xe7\x12Q\v\u04a4\\uc*\xbe\xf93\xe5\x8f&\xf4\xcd8\xcd\xef\r\x00\xd2\xe0\x8a\xa6\x99\x155\xeaWj\xdb\\J\x95K]iRe`V\u07c8\x13\xb2\xad1O{\x00\xdbT&)mO{|@u\xaf\x8bQ\x97\xccsz\xa7\xe5f\xe2\xc1Z%_\x12(\x13\xc5\xde\xf91>O'\x02#\x04\x03\x05C\x0e\xa2t\xb0V\xf0fl\xf1\xa4E\x8a\x91\xe7=\xd8a\x1f\xf7}\xf5\xb3\xb8\xe3\u068fa\xf7\xc3?\x85\xd2\x1d\u007f\xaa\n[\x89\xb5\x8583 \u034c\x13O>\t\xe7\x9cs\xceg\xde\xf3\x9ew\xfd\x9f\x87\x1ez\xf8\xfd\x00\xf0\xaew\xfd6\x9d}\xf6Yr\u9957\xfd\xcc^\xef\x130\u07cf\xd7\xfd\xf7\xdfO\x00\xa4(\x86'\x0e\x87\u00e3\x17\x17\x17\xc1\xccdm\xf4iv\xc8r\x8d\u00f7\x9c\x8a|z\x16\v\xfd\x81?\xc6\x06\xdbP'M\x1e\xb5\x91\x05\xb9V\xc1\xab\x02\xa8\xc7\b\x1d\x97\xfc\xbcC9\x86/\xa5\xa7t%\xed\xa3\xa6\xa1\x9f\xa4\xbc\xb0\x946\"13\xa2\x04\xb4\xf8\bc\xd2\x10\u0575t\x94\xec\x0e\u049c%O\xe2\u07e9^V\xd6\xce H(\x8b\xb5\baj\x96\xe4q\x8a\xb2\xac\xdd\u00c8y\xed%L\x9e{\x94(rL\u04a31\xfb\x15\xa0\x95%\xa9\xfb\xbd4\x8a\u548f\x96\xd6B\x1a\x82q\x9a\xa5\xc9\xe2\xd4\xda\xce\r\x11P\xaa\x9e\xe1&M\x13$\x8b\xb1\x1a\xd7T\x9d\xb9\x84\xaasX\xf4\xcd1a,_H`\x89!*\x03\x87f5\xfb@(\x88#\xa8L#\u02fbXz\xe2A\xdc\xfd\x85kp\xff7\xfe\t\x83\xa5y\xa8\xac[\xebS8;\x92b\u0627^7\xc7\x19g\x9c\x81\xcb.\xbb\xec#o}\xeb\u06ee\xf8\xe0\a\xff\x02\x17]t\xa1\xfa\xe7\u007f\xbe\xc1\x12\x91\xfc\xac_\xef\x130\u07cf\u05ed\xb7\xde\n\x00\xf8\xeew\xbf3\u06b6\xed\t\x93$\x8c\xfb\x0f\xb9\x00y\xa7\x87\xf5\a\x1f\x8a,Wp\xcb\x16\xb6\xf4\x84\x1e\xbf\xc0\xc7*\xb8\xe4\xf8.i\u015ap\xc4\x12\xc0\x9dR\x1e=\x863KmN\xbd<-\xd4F\xbe\xa9Fs\x8f\xc9\xff\"\xf7\x1a\xdd\fE\xa4\x1a3O\xe5\x13\x89\x96\xbc\xae\x90\xa9N\r\t\x05_\xca\xdc\u028a|\u0301\xb0\xd1\xe0ly\x81\xa8\x85\x18\x1a\x93\"6\x90\xb9m\xf0\xa6\xed\xd0@\x18\xf7\xe8*\xc1vM\xe2\x1a5#.iT\xeac-\x80\x96;\u07db\u0405\x92\xcfN\t2\x04d\u0283\xb9&oM\x1b\u007fVQ\xf5Yt$p\xe2-~c\x18\x92\x84\xe9Md\xfe\xf5c&(\ue049Q\xf4W\xf0\xc8M_\u015d\xd7^\x8dm\xb7\xdf\bg\r\x98\xb3*l\x1a\x04g\x8d\f\a\xabt\xf0\x86\x83p\xca)[\x17.\xbb\xec\x17\xdf\xfe\xa67\xbd\xe5o\x01\xe0O\xfe\u43f2w\xbe\xf3\xb7\v\"\xda/\xae\xf7\t\x98\xef\xc7\ub847\x1e\x02\x00\xdc~\xfb\x1d\xb4\xb8\xb8T\xe5D\xa6\xd8\xe9\x04\x9c\xe5\xc1\a\xc4WK.a4\xdcZ\xc7p\xaa\x83\xebZ\xd7\xc3x\x85G5>\xb5f\xce\x1a\x80\xbef\xe9\x9aR\xda\x01\xa0]\xf2\u007fI<\xa9\xaf\xf6\x1458\xe0D_\xbd'\xc1s\x8b\x11UIaP;\xbe\xd5\x14\x1fi\x1a\x8fT\xd5p\n\xacT/\xe21\xa6\xf2\xdc\xd3\xc6\u0664J\xd2j\xba\xe1\u007f\x024\x95#\xed\xef\u025e\xda\a\xa9\x8c\xb3mci\xda\xd8R\vM\xa3\u0607HDJ\xc5\x03\xbd #\xf2\xa1\x12a\xe0G U\xe0r\x8d\u0593\xe4\xc4%Py\a:?\x00\xa6\xbf\x84\x9dw\u078c{\xbe\xf4I<\xf0\x8d\xcfbe\xc7c`\x95y\u014a\xf3\u039d\x9e\xa6rR\x8c\x86t\xc4\xe1\x9bp\xce9g\xcf_y\xe5\x1b_~\xdey\x17|\r\x00~\xef\xf7~\x976o\xde\\\xecO\xd7\xfb\x04\xcc\xf7\xe3\xd5\xef\x0f\x00\x00\xcb\xcb+0\u01b46\x02\xfb\xab\xcb\u8bec\x04\x06\x84a}\xf9\\S-H;\x05\\\a\xa5\xe4\xfbM\u0395\u05a8\xea\xeb\xf5jThP\"[\xab\x98XnX\x8dx'\x01\x81\xb3\u0271\xdf5C\u0712Z85\x9cj\xf3\v\xa7\x96J;\xf9y\xda\x03\r\x9d\xe2\x0e\xed\x85\xf2Hi\x8a6\x00LM\xb6\xa8\xc1\xd9K\xe3\xf5\x96\xc6\f}\xbbU@\x02\xcc2\x0e\xf6m?\x9b\x9a;\x8eY\xd36\xfe\xa4\x96\u075b \xa1\n\xf7i@\xf1\xb8@\x94\xf8\xd3\v` q\x0e\xcd[\n\xa3jZ\xa7Q\x81\x00!\x9f\x9e\x83\xd8\x02\xbb\xef\xbd\r\xf7~\xed3\xb8\xe7\x8b\xd7`\xe1\xe1{\x82\x0f\x0e{7c[\xf8\x01\xb8\x80\xff\xa6\x18\xd2a\x87m\xc4%\x97\\\xfc\xf0o\xfe\xe6Uo8\xe5\x94S\xbf\x06\x00g\x9f}6\x1dq\xc4\x11\xf2\xcaW\xfe2&`>Y?\x13kzz\x1a\x00\xb0n\xdd\x01\xd0ZS\x1b\x98[\x01\x1e\xbb\xfb6<\xfb\xdc_\x84\xca2\xc0\x8cJ\xa0qu\xf4+\x8bT'-<\xed\x1eh\x81\xf1\n\x93BVg2Q\x1a\xf9\xe1d#`\xd4\xef75dt\xf0\xde\x1c\xa4\xe3\b~x\xbc\x8d*\xb8\x1cj\xa2\x06O,\xb4f\xb5\xdd\x046\xa0\xddKdOTC:H\xda\xe4\xe2\xa9\xed\xa4\u04e0^\xa2\x94\x9dZ\xee\xdf5*zY\u3157f%\x1e\x01\xbf\xa5\xe1K\r\u03bby;{z\xee\x94\xd0m\x8a\xbc\u02e1\xa2\x94/\xf7wl\xc5W\xe2&\x98_9T\x8a\x14'\xa8\xbd\xd7\x11\u01f9\xd3\x05\xeb\f\xbb\xef\xbf\x13\x0f~\xe3\x9f\xf0\xd3/}\x02;\xef\xb9\r\xce\x14\t3\xe7\x1b\xf7\xc4\u02a3\xb83B\x04\u06b2\xe5D\x9c~\xfa\xe9\x9f\xfd\u0407>\xf8V\xa2\xfc!\x00x\xc5+^N\xaf}\xed\xe5\xf2\xf2\x97\xff\xd2~w\xbdO\xc0|?^g\x9du&>\xf0\x01\xe0E/\xba\xa4\xf3\x0f\xff\xf0?r\x9f\xe03~Y\xfe\xf0\xf3\xff\x88\xe7\\\xfcj\xac?~\v\x86\xa3\x11\x8ci\ua269\x9e\u053e\x06pK\xa3Z\x17\xa9Q\xcc\xe9\xa19\x0e\x1d6\xc2\x1a*q\x9f\xa2\xa4\"\x0e\xa0j\xa5Q\x95\xa2r\x1etX\xeb\u030f\xb1\xc6\xdfZ\x15\xf6\xbfF\f\xb2'*\xa9\xf5\xb4\xd22h\x93\x16\xff\xae\x85\xedi\xbb\u03f1\xf9$Z\x9b\xc7\x1es\x14h>\xb6\xa4\x1a\x8eT\x9a\uc067\xdf\x13\x8d\x1e\xe9\x16\u037e\x12\xd7\fd\u025b\xee\xc4\x0f\xf9\xd8p\x92\xaa;$W\x03K.\x8c\xe9#PcJi\xa8\xbc\x8b\xfe\xeem\xb8\xef\xeb\x9f\xc5=_\xbc\x06\xdbn\xfb\x1e\u0330\xdf\xf2\xfa\x87\xe9_\x888k\xe9\xc0\x03\xd7\xd3\xc9'\x9f\x8c\xf3\xcf?\xef\xaf\xdf\xfd\xee\xf7\xbe\x93\x88\x16\x01\xe0\x8f\xff\xf8\xfdt\xee\xb9\xe7\xca\x19g\x9c\xb5_^\xef4\x81\xbc\xfdw\xddz\xeb\x0f\xe9\xb9\xcf=EDD\xbf\xf0\x85\x17}\xfe\xc6\x1b\xbf\u007f\xd1\xc2\xc2B\x01 k\xfe\xec9\xaf\xfb?\xf0\xa2w}\x00\xab\x05\xb0k~\x11&\t@p\xad\xf9\xedT\x06\u8d8dl73}\u04df+\x81<\x06\u0460R\u03a4\xe0\xe5\x12\x00\x8c`\xdeVIK\x8b\xeap\f@\xf1\xaf\xb4_\xc5\xda \xb9\xa7\n\xbc\xf5\xff\xa5\xae\xf2\xc0\x1a\x8f3\xd5~G\xa0N)\x13Yc\xd3L)\x1d\xfaW<^iy({\xba\x8dh\xa6\x16\u04d5\xe2\xfb\xc0I\u04d9#\x80\aKZ\x1d\xee\xa0pQ^X\xf5`\u01ac\u0723\xc6'\xdc9\xb3\x02k\r\xcer\x10\x80\xc7~\xf0u\xfc\xe4\xd3\u007f\x8d\a\xbf\xfd\x05\f\x97\xe6[@<L\xff\x86\xb8\xb8<\xd7x\u05b3\x0e\xc1\xf3\x9ew\xca]\xafz\xd5+\xff\xecW\u007f\xf5\xf2\xbf\x01\x80\x0f\u007f\xf8o\xf8\x8a+\xaet\xfb\xfb\xf5\xce\x13\xc8\xdb\u007fW\x00r\"\"\xb3i\u04e6o\xcf\xce\xce\x02Q\xf1\xd6X\xffr\xcd_\xe0\xe6O\u007f\x14\x9c\x01y\xb7\x97\\\xe4\xf5\x9c\xb3(\t\x94\xa0Q\xe7\x16\x8a\xa2\xa6hi)\x96#?\xad\xaa\x10\xf6\x9a\"\x06 \x98p$w\x12+G\x1a?U\x10\xad\xcd\xddJ{UL{\xa1\x81\xfeU\u07e3J\xf3\x9d\x98 \x8e}I\xe3g\xc6B{\x9a\xaf\x1daL\u07ce\x96\xd7xlR\u007f/\x95\xf4\xda\xd56\x95\x1b\xa6j\xde>\xb5\xbcv\x84\x92\v\xcf\u060f\xe0w\x15\xa1\u02c4\x9c\xa3\xb4P0r\xc0\xc8\tFNj@.\xd2\xdc\xc0\xc2ILk\xe4\xbd\x19\xe8\xde\x14\x88\b\x8b\x0f\u078d\x9b>\xfc>|\xed}W\xe1\xee/^\xe3\x81|,\xfeNy\x97O\u71cb\x0e<p\x1dN;\xed4\xbc\xfa\u056f\xba\xfe\x03\x1f\xf8\xc0\v#\x90_}\xf5G\xf9\x8a+\xaet\u007f\xfe\xe7\u007f\xba\xdf_\xef\x13\x9ae?^_\xff\xfaW\x11\xf5\xb3o|\xe3\x15\u07f9\xe5\x96[\x17\x1fy\u4479\xb6\x13\xbd)\n\\\xff\xa7\xef\x84\xd1]\x1cs\xf1\xaf@\x15\x05`m\xb8\b+\rw\x8a\x95\xc1\x03z\xbc\xb1\xd6\xc2\x17\x8c\xe5\x05Q\x18\xb2I6\b\x97T\xf6q\xb3\x18\xcf\xf8LO\x002V\x1d\xcbZ\x1c\xc3^\xa8\x92\xbd\xfdl:\xc9\u0674\u007fM+\u0174\xc1\xd7\x06\xde\xd8\x03\x95\xd2<\xcdP\v\xfdB{\xa4=R\u05d3::Sku^\xd1]%\x9d\x95\x9a`\x05N;>\x1e\x9dLmj\xf6\xd6\x03L\xf5\n\xdc\x04N\xdco\x9cT\xdbW\x89\xc2\xe8=|'Ck\r\u055d\x02 \x18,\xec\u0093w\u0742\a\xbe\xfd\x05<v\xf3\u05f1\ubf9f\xc0\f\xfa\xcd]\xbe\xf2\xdcwN\xc4\x14t\xd0A\a\xe1\xb0\xc36\xe2\xc4\x13O\xfc\xf1\x05\x17\x9c\xff\x97W^y\xd5\xdfGZ\xe5\xdak?\xcb/}\xe9/\xba\xcf|\xe6\xd3x\xd9\xcb^\xb1\xdf_\xef\x13\x9a\xe5\x19\xb4~\xe37\xde\xf8\xd9\xcf|\u6cd7n\u07fe}\xbc\xd4\f\x17\xcb\xdc\u01a3p\xc9\xef}\x14\x1bO\xf97\x18\xf4W\x82\xd6\xd7\a\x00\xa4:\xb8V}s\vHq\nz\x01\x8f\xe3\xf0\b\xc1{q\xb8\xd0\xf8L\xaf[\xb7\x87\x90\x9eVj\xa7\r\x19[\xa8\x96\xff\x15:\x05-\x95jM~\xb8\x87\x8d\x80\xf6r\u007f\xf2\xff\xe3B\xac\x816%\x96\a\xb2\x97'\xd6\u0417\xb7\x9d\n\u048d$\xddDJks\xf2R\xc3\x0e\xd7\xe7W\xad\bF\xb6\xeag\xc4|\xcex/5\x9aM\x00\u07f7ad\xd3\xd3`\x01V\x9f|\x02\x8f\xfc\xe0k\xf8\xe9\x97>\x89\xc7\u007f\xf4]\xac>\xf9\xc4\x1a\xaf?\x05\x1fr\xb2\xb6\x18\xb9u\xeb\xd7g\xc7<\xfb\xd9\u063c\xf9\u87dey\xe6\xf3\xff\xdb\xdb\xdf\xfe[\u007fCD%\xa1\xfew\u007fw5]~\xf9\xeb\xe4\x99t}O\xc0|?_\x9f\xfc\xe4?\"J\xb0\xbe\xfa\xd5/\x9fu\xd5Uo\xfa\xd4\xddw\u07fdqO\xbf\xb3\xf5\x97\u07ccs\xff\xaf\xff\x86a\xbf\x0f\xe3\xbc\xe6\xc0%~\xdee\xf5\x89\xb4\x8a[;\x977\xf5\aA\x00s\xae\x02\xdek\r\xb8\xe8\u0451zm\xd5\xf4\xdb4\x0e\xf0.i\u6943N\xa9\x94\xb2)\xb3k\x1bY\x97\x96*|\xefT\xc5\u068a\x17\x91q*\xa6mx\x96\x1a\xb4Jk\xc61\xa1\xe6E\xd2\xd4\xfbKc6\xaa\x91i\f\x91\x8a\u058a?\xdf\u059c\xe6@\xa3\xa8D\xaa\x19u\xe21\xc4\u00c6\xc1\x1e\x13\xbc\xe6\u0460Q$\xd26Toz\x131\xf2\xde\f\xc4\x19,<r\x1f\x1e\xbf\xf5;\xb8\xff\x1b\xd7\xe2\xe1\x1b\xbf\x8c\xe1\xf2\xfc\x9a N\xac\xbc\u07fe\xb5\xae\xd3\xe9\xf0q\xc7>\x1b'\x9cp\xc2\xe3\x17\\p\xde'\xae\xbc\xf27\xff\x84\x88\x1eM\u007f\xe7\xc6\x1b\xbf\x87\xe7?\xff\xccg\u0735>\x01\xf3g\xc0\xfa\xad\xdfz\ab(\xed\u55ff\xe6=\xd7_\xff\u03ff\xbfc\u01ce1\xfca\xe5#\xe3\x9e\xf5\x9c\xad\xb8\xf4?}\n\a\x1c\xbd\x19+\xbb\x97|\xb2O\xe9\xa1RM\xbb\xa4\x81p\xcdj=\x8eo\xa3\x99l\x8fj\x80\xa4\xe4\xe0\x13\x80\xb1\rpX\x8b\n\xdf\xe3\x9c\u0358\u007f\xeb\xdaMPZk\x8eH\xc6\xf9c\xd9\x03\xc5Q\xbb\xfd\xda\xff'\u05b9\te\xd5\u49db\xf7W5\x1b\x83\xdb\xe0\x1a\x9b\x95\xb4\x9cL\u0186_\xd1p/\xa0\xea=\xe2F\u056e\x89\x02\x95\xe2}T\xac\xab\xde,\x17\x14)#G\xb0.\xd17%Fk\xe9f\xcb\xf1\x15P\ny\xb7\v3\x1aa\xe7Oo\xc3#?\xf8:\x1e\xf8\xe6ux\xe2\xf6\x9bP\xac.\xb5\x03Sp\xe3,CK 8\xe4Y\a\xe3\xb8\xe3\x8e\xdbu\xce9g\xff\x8f\xdf\xff\xfd\xf7\xfd\x15\x11\u0756\xfe\u03bd\xf7\u0783g?\xfb\xd8g\xecu>\x01\xf3g\xc0\xfa\xc8G\xfe_\xfc\xfa\xaf\xbf1\x80\x89\x1cp\uee7f\xf0\xc9o~\xf3\x9b\x17:\xd7\xeeq\xca\xcc8\xe1\u016f\xc1\x19W\xbe\x17sG\x1e\x8b\xe1\xb0@Q\x14\x10g\xe1b-^*Y\xa8\x01\xec\xd5m\xa9\x84\x03\x88\xba\xe3X\u5e44#\x8f|y\xe4mC&\xd1X\xc5\f\xaa\uedd5{nC\xf9\xa6\xcdk\v\xa72\xc6}\xaf\xc5\xe7\xa0e\x02\xb26\xfe)\xf5\r\xa3\xb4}\xa5\xca\u05e6e\a\x8aI;M@\x8f\x9e.\xdeU\xb0\xb1\xb1H;\xdf.\xc9k]\xbe\xee\u912bT NR\u077f\"Ow\xe9\xa0\x15\x8f\x16\u0205\x93\x92B\x89\u0787\"\xd28uH\xa9\x18W\xd1\x06\x81\t\xac2\xe8N\x0ek,\x1e\xbf\xed{\xb8\xef\x9b\xd7\xe1\xc1\xef\u0740\x9d\xf7\xfc\b\xb6\x18\xb5\xa2Q\x19p\x12\xde\xebLkt{\x1d\x9cx\xc2\t\u063a\xf5\xb9?\xb8\xe0\xfc\xf3\xdf\xf1\xcaW\xbd\xfa\xeb\xe9\xaf\xfd\xdd\xdf]\x8d\xcb/\u007f\xdd3\xfe:\x9f\x80\xf93d\xfd\xf8\u01f7\u2913\x9e\v\x00\xf8\xda\u05fe\xb2\xf5\xfd\xef\u007f\xff\u05ef\xbf\xfe\x86\xb9Vr84\x167m=\a[^q%\x8e8\xfbE\xe8\x1e\xf8,\x18\xe3P\f\ap\u0468\xab\x85w\x8d\x00V\xafM\xa5\xf4\xb3\x8eG\xf8\xf8;&\xe1\xc7K\x87\xbb\xa4t\x96\x96t\x84\xb1\xea\xbcm\x80\xa6\r\xe9Z\x88\xf65\xbdRP\xc9\xef\x9a\x00\xdb\x1c\xe0\u065b>[jS\xad\xd5\xf3\xad\x03yx\xf6m\x8a\x97\xb0\xf9U:q\n\x95\xaf\xff\xa6kP[Q:\x18+\xed\u0606\xb4e\xe5N5\xc0\x8f\xf7i\xa4\xd2RF\x8f\x1eA4.KN\x19\xa8d\x89\x11\xc0\xe3\xaeKD\xe0,\x83\xce;p\xd6b\xdb\x1d?\xc4\xdd_\xfe\x14\xee\xff\xe6ux\xf2\xbe\xdb\xd1\xf4\a\x8a\x0f\xa0\x04p?\xf5C\x9dN\x8e\x99\xe9\x19l\u06b4\t\xcf{\xde)\v'\x9ex\u009f\xfc\xd6o\xbd\xf3CD\xb4\v\x00>\xfe\xf1\x8f\xf1k_\xfbknreO\xc0\xfc\x19\xb9n\xba\xe9\xfbt\xdai\xa7\v\x00|\xe2\x13\xff\xf0\x92\xff\xfc\x9f\xff\xeb5\xdf\xfe\xf6wf\xdb>\x16\x14F\xfa\xa7\xd6o\xc0\xc6\u7783\x13^\xf6F\x1cv\xea\xb9PS3\xb0\xc6\xc1\fVk\x8d\xae\x8a\xb2\x90\x06\x8f^\x81I\x12(_\x1bXI\xc1\xbcf\xb0DU\xd5O\r\xad\xf2ZMKip\xed\xcd\x1fn\xca'E\x9a\xbe1\x82*\xbb\xa1\x82cn\xb1*H=l\u02a1\x9e\xf0}\xa6\xf1\xfec\x94]J\xa8d\u02f0\x86\xe4a\xa6\x1b\x9dC}X\b\t\xe0\u01e0\xb44\x06/Fw\xc6\u7903\xc3c\xf4\b72\xde\xe5\xb0R\xdf\\R\x8a\xab&-M^\u0426\xb5B\xa9Ya\x8dNo\n\"\x82'\xef\xbb\x1dw|\xe1\xbf\xe3\xbeo~\x1e\xbb\x1f\xb8\x13f4h1\x81!\x1f$\xce\f\x11'\xe2\x84\xe6\xe6f1;;\x83C\x0e9\x14g\x9f}\xe6\u03a3\x8f>\xfa/^x\xf1%\u007f{\u0496\x93\x1e\x04\x80W\xbe\xf2\x15\xea\x13\x9f\xf8\x94\xdd_\u0331&`>Y\xff\xcb\xeb\xaf\xfe\xea/\xf8Moz\x8b\x03\x80\xab\xaf\xfe\u0605\x1f\xf9\xc8G\xae\xf9\xcew\xbe\xbba4\x1a\x19\xa5\x94\xb2\u05b5\xea3f\x0f9\x1c\x87\x9dz.\x8e\xb9\xe8Wp\xe8\xd6s\xa0{3(\x06\xabp\x81\x96i~\xa4\u04a37S\x134+d\xa5\x06\xa7]w\xf1\xab6\x95\xa6\xba\xa5\xc9\x17\xd7\x14\x19M\xab\xdb=\xad\x06h1\x118\xdcge\x0e\x15\x81\x8f\xcaA\xa7X\xc4:Y;u'\x1d\xb2\xa9\x01:\u0579\xf9\x96t\xb9\xbd6C}\x90\x03\x95\x9bb\xc6\x04\x15\xc0\x19\xa5\xc5,\xd5\f\u04cc\xabd\xa4\xae\xda&\x91\xe4\x8d\xd4\r\xba\xa4\x02o$\x9b\x05\xa5\xd2$q0\u0387C\xe4\xdd\x0e\x96\x1f\u007f\bw\xdf\xf0I\xfc\xf8\u068fc\xe7Oo\x83\xb4T\xe2Q\x99\xa2\x98\xc59g\x8c)p\xd0A\af\x1b6\x1c\x8c\x83\x0f>x\xfb\x19g<\xff\xd6\xd3N;\xf5\u00ff\xf2+\xbfz\x1d\x11-\xc7\u07fb\xfa\xea\x8f\xd2\xeb^\xf7z\x99\\\xc5\x130\x9f\xac\xb0>\xf4\xa1\xbf\xe4\xdf\xfc\xcd7;\x00\xf8\xf4\xa7?y\xc1\xd5W_\xfd\x0f\xdf\xfa\u05b77\xec\xdc\xf9$\x98\u0649\b\xa7>.13\x93\x94\xc6\xf4\xc1\x1b\xb1\xf9\u0717\xe1\x84\u02ee\xc0\xfag\x9f\xfc?\u06fb\xd2\xe0:\xaa3{\uef7d\xbcE\xfb\xd3\xf2$\u02ca6\x8c\x1d/x\x19\xc6\xc66v\x8c\x1c\x98\x94\xcb\xd4\f\x1e\x02\xe4G25E\xe18\x19\xa8\u0250T`H\x82a\x96\x84Jf\x8a\u0270XP@\xa8\n!\xc9\x04\xc8x\x02\x89\a\x13\x1b\xe3\x15\x19[\xc6c\xcb\xd6bY\x8b%\xdbOz\u04b3\x96\xb7t\xf7\xbd\xf3\xa3\xbb_w?=9T*\x10\x92\xf4\xf7G\xb6\xf4\xd4oQ\xdfs\xbf{\xbe\xf3\x9d\x0f\x86a@OM[G\xe5\x1c\x8b\u05dc\xbc\xd3[\xfa\x83\x8bs\xc7\fj\x06\xee6}\v\xa0DNES\xe4aT\x80Y\\\r?\xc0\x02p\x80\xdb\x04t\x99\xda\xca\x0e\x93;\xd6sN\x10N\x929s\xd7\x103\xb2~GuCs\x00\xde\xfe\xbf\xbd9\xe4\xb6\xf0\xbb\xaf\u0248\xa3\xef'\xd9l\xdetx\xb4\u01aaZ\x80k\xda\xc9\xdat\x89\xbb{\x96{\x8a\xc1b\xc6)\x04\x96~\xdcV\xbc\x10\x97g\x8b\xd3[`\x1e\x9f$E\x85\x12\n \x99\xb8\x82\xc1\xf7\xf6\xa1\xfd\xa7O\xa1\xef\u0777\xf2r\xe2\x84R\xd3\b\x8b1\b\xce\rn\x18,\x12)CCC\x03\xaa\xab\xab/,\\\xb8\xf0\xf5\x96\x96\x96\xc7ZZ6\x9es\xff\xde7\xbf\xf9\x10Y\xb6l\x99\xb8\xed\xb6\xbf\xf6\x17\xaf\x0f\xe6~\xe4\u01b7\xbe\xf5M\xf2\xe8\xa3\xff$\x00\xa0\xad\xed\xf0\x86\x1d;\x9e\xf9\xcf\xfd\xfb\x0f,<w\xee\x1c\f\xc3\xcc\xef8w(Ij\xb5M\v\xc1A)Ct\xc9\r\xf8\xe4_\u074dO\u0738\x19\x81\x92\x12h)\x1dF&\x85\\\x8d_>s/\x01\xaf\u0305\xe4\xe4\xa2\xc45\x9d&\x97\x1b\xcf*'0\xcb88\u0324C\x90\x876\u0235\x17\x80\u02e6\xd56\xf9\xa2\xc4\xecj\x14 \x969\x94p\x81\"q\x15\x05\x85\xd7\u070b\xe4Q\x9c\xe4\xd0\x14\x94\x98\xf2=B\xecY\x1e\xf6\xa7 r\xe4\x92\xe6\xc6B@\xac\xe767\x99,\x03\xef\xca\xfeM\x1bY1\xc3\u01c6\xbbOA\xc8\xd5\xec\v\xab\x17\x80xL\xbe$\xd7H;\xee\xca\u0429e\x8aEe\x19JH\x8561\x81\xe1\xe3\xef\xa0\xe3\xcdW\xd0\xf5\xf6N$\xc7F\xf2\x838\xa1`\x8cZ\x9b\tGEy\x05\xe6\u039d\x8b\x9a\x9a\xe8\xb1\r\x1b6\xec\u07f6\ud2ed\x8a\x12<\xed\xfe\xbd\x05\v\xaeEkk+\u05ad\xfb\x94\xbf`}0\xf7\xe3j\xf1\xe0\x83_'\xdf\xfe\xf6cV3\xa5\xa8\u07fe\xfd\xe1\xc7\xf6\xed\xdb\xf7\u064e\x8e3\x88\xc7\u3982\u015d\xa1Sj6n\x00\xe0Z\x06\xc1\xb2J\xd4\u07f8\tsW\u0742\xaa\xeb\u05a00Z\vC3`his\xaa}\x0e\xaa\x12\xe1:\xda\xc3\xd5\xe5\txG\xb3[w\xa6c\x80\ubc3c\xd9\xd6~kX\xb4\x10\xb3\xdf\xd0\xf9\x9a\x98\xf2\xb5\xc5S7\x9dB\\\xad\xed\"\xbf.<WS\x9d\xcb\xc7\xcf\xec\xd1\x17N\x03\x15q\x0f7\x16\x96\xae\x9bf\xb5\xf7\x0e\x95\"r\x9as\x9cL\x99\xbb^\x89)\x17$Y\xe9\xa3]o0\\j#\x87\xa3\x9f\xf99\x10\x17}\"\x11\xa7\x11\x88YER\xcd\xdap\x19e`\x8c\x81\xc92\x88\x04L\x8f\xc6\xd1wd7z\xf6\xfe7z\x0f\xfc\n\xc9D|\x16\x10g \x14`\xcc\x04\xf4\xca\xca\n,\\\xb8\x10\x8d\x8d\r\a\x1b\x1b\x9b^\xfa\xcaW\xfe\xe1g\x84\x10O\x17\xdb\xf7\xbe\xf7]|\xf5\xab_\xf3\x17\xa8\x0f\xe6~|\xd0x\xe2\x89\uf8e6\xa6\x06\xf6\xf1U\b\xa1\xb4\xb6\xee\xd8x\xf0\xe0\xc1\xad\xdd\xdd\u0777\xf6\xf4\xf4`ll\f\x99\x8c\xe6\x90\x1d\xd61\x99\x10\nn\x18\x10\x82#TZ\x89\xaa%\xabP\xbfv\x13\xeaV\u007f\x06\xe1\xca90t\x1d\x86\x96\xb1\x94/\x0e\x0fmg\xb2\x1e\x05\x8a\x98Y,txh/Q\x92\xab84D~je6\xfa%\x17\xe0i\xce\x00\a\x96\xd3\xfa\xee\x06k7\xbfo\xcb\xf4`78!\xff\t\xc0\xcb\x15;\x198\xb1\u032b\x18\x00j\x8d\x88c\u01247l\x8bX\xf7k3r(\x9e\xecf\x92;\xddIx\u007f\x0e\xe4\x11\xf3\xb8d\x89\xcc\xea\xec\x94\b\xc9\xcaE\t1\xff\xc6L\x92!@\xc1\x98\xf9\x1a3\x93\x93\xb8|\xf6\x04\xfa\xda~\x8d\xc1\xe3\xfb1\xf4\xfeAd\xa6&g\xbcIJ((%\xd6\xf0n\x01\xc6\x18\xa2\xd1*\u031f?\x1fUU\x95\xad\x1b6\xdc\xf4\xcb/|\xe1o\x8e\x10B.\x02@M\xcd\x1c\xf2\xdcs\u03c8\x13'N\xa0\xa9\xa9\t\xb7\xdf~\x87\xbf8}0\xf7\xe3\xb7\t!\x04\x1eyd;\u06fe\xfd\x11\xc3\x06\xf5g\x9fm\xdd\xd2\xd9\xd9\xf5\x8d\x83\a\x0f~\xb2\xa3\xa3\x03cc\xe3\x0e\xa0[]y\x84I\x96\x1e\u06000\f\x04\x8a\xcbQ\xb5\xf0\xcf\xd0t\xd3\x16\u052d\u0744\x82\xf2J\xe8\x9a\x01-\x9d\x02\x11\xe6du\xe1\xd2,{\x00\xd0C18\x15S\xe2\xa1f\xe0\x9a\xa4\xe9\xe2\xcdg\u0443_ej\x9a\xa7\x89\xc6]\x90\xa49\x13\x1e\xf290\xba\x1bw\x04f\x1f\xe2\x90\u07e4\xcb\xedDh\xfa\u04d8\u067a\xc8z\x9d\x88<\x83\x96!r\xadj\x9d\x13\x8e]\xa0%\xae\xcf\x17\x989\xc9\xc8\xdd4\xc4\\\x9b\vc\x14\x8c\u0240$;:|\xc1a\xa4\xa61y\xf9\x02FzN\xe1\xe2\u98f8\xd4\xf1\x1e\xe2\xe7;\x91\x18:\xef\u027e\x9d\x8d\xd1j\xf2!\x14\xdc\xd0\x05c\x8cD\"\x11,X0\x1f\xcd\xcd\xcd\u03efX\xb1\xe2\u026d[\xbfx\xcc~\xfc\xe2\xc5K\xd8\xf3\xcf?+\xae\xbf~\xa5/1\xf4\xc1\u070f\xdfetu\x9d\xa1\x0f?\xfc\b\xfb\u044f^\xd6,P/|\xee\xb9g?\xbfw\xef\xde\a\xde}\xb7\xad\xb6\xbb\xbb\an\x1e\xdd\xee\u04a3\x8c\x81P\t\x9c\xeb\x10\x86\x8e`I\x05j\x96\xaf\xc3\xe2-\xf7 z\xddZHA\x15\\\x17\xa6\xfa\x85s\xcblIx@z&\x14z=@\xdc\xee\x8d\"\xcb\xc5\u007f\x10W\x15x\xb8x\xb7$\u041d1\x13\xfb\xf4\x007_\rOJ\xce-\x030\x8f\xdcp\x96\x85$f1~'y\xb6\x1a\x87~q\xb6,\xe2\xfa\xbe\xbbx)\xf2\x8c\x87\x13\xaeS\xc6\f\v\x84\xec{4\x87hg\a+\xcb2$E\x81\xa4\x98\x04|r:\x03#=\x8d\xd4\xf8(\xc6\xfa\xbbp\xf9l;\x86\xde?\x84\xf1\x81nL\x8e\\B21\xea=\xd1\xc8\n`\xdd\v\xc4*~\x9b\x1b\x91\x10\x86\xa1\x93\xf2\xf2\b\xaa\xaa\xa2X\xb2\xe4\xba\xf77o\xde\xf4\xe4\x1dw\xdc\xf5\x8c-'\xbc\uffbfS\xee\xbc\xf3\x0e\xe3\x86\x1b\xd6\xfa\x12C\x1f\xcc\xfd\xf80\xe3\xc0\x81w\u021a57\nW\xe6\x1e~\xec\xb1\xef<\xbeg\u03de\u03df8qB\xb9x\xf1\x12\x00h\x84\x10\xd9\x01\v3+\x03Hv\nLQM=\xea\xd7nBs\xcb\x16\x945.\x84ZRf\x0e+H&\xad\xe6\x11\xc7\xff\xd6\xe3o\r/\xf0e\x8b\x9d9z\xf1\xfc\xae0y\xb2qO\xf1q\xc6EL\xbe\x1a\x8e\xb6\xdb),\nO\xd3\x0e\x17\xf0(\xc3E\xbe4|Vt\x9f\xc9\x059\xde5N\x11\xd8,\u019a[\x8e\x9d\xf3r\x97\xcc3\xf7rY\xeb\x81\xec\fW/\xc9D ,\x9bXU\x91A\xe4\x00d\x85\x82gt\xa4'\xc60=2\x8c\xf1\xc1s\x18>\xfb>bg\x8ea\xe4\\\a\x92c1\xa4\xa7&\xc0\r}\xc6\xe7I)\u02f6\u061b_\xad\xb1mB\x18\xe9t\x9a\x97\x95\x95\xc9s\xe6\xd4`\xfe\xfc\xf9\xf1\x95+\xff\xfc\xdf\xee\xbf\xffk\xdf%\x84d\xc7\x02}\xe7;\xffJ\x1ex\xe0\x1f}y\xa1\x0f\xe6~|T\xf1\xea\xab?\xc3\xee\u077b\xc9SO\xed\xc8.\xbcw\xde\u0673\xf1\xe5\x97\u007f\xf2\x8d\xe3\xc7O\xac\xef\xec\xec\xc4\xe8\u8a1d\x9cR\x0f\x8d`\x1d\xbd\x05\u7812\x84py\r\xe6,[\x83\xba\x1bnA\xe5\u0095(\xaem\x02Q$\xe8i\u00f4\b\xe0\x1c\x82\xebV\xd6\xeed\xa6\xe6\x14!W\xcb\xd1U-\x02\xf3\xd3(\xf4K\xd5\x00\x00\v\xecIDAT\"\x1e\x83+K\x8d\xe1\xc9\xfc\x89\xfd\xe2g\aL\x9b\xe20r&M\xb8\x9b\x99f{\x1dW\x1b\xd6\xecH\xfeL\x12;+[\xb4~n\xb8\xb2\xf3|\x1c8r\b!\x02\xaf2\x881\x06EU!)\x12\x88\x00\u04898&c\x171q\xb1\x0f\xa3\xbd\x1d\x88u\x9d\xc4\u04296\\\x19\ua0de\x9a\xc2\f{\a\xf7\xe9\x8b8~\xf2\x0emC@\b\x11\x9a\x96\xe1\xa1P\x98\u035bw\r\xea\xea\xea\x06\x97-[\xba\xf3\xcb_\xfe\u04bfG\"\x95=\xeekuvv`\u07bc\x05\xfe\xe2\xf2\xc1\u070f\xdfG\xb4\xb6>\x8d\xad[\xb7\xb9\x8e\xf9B~\xe9\xa5\x1f\u07bdk\u05ee;\xfb\xfb\a\u059d<y\x12\xf1x<\xef\x8deN\x81qRc\xb5\xa0\bU\v\xaf\xc7\u0715-\xa8\xb8v\x05\xd4\xd2J\xc8\xc1\x10\x94P!\xd4\xc2\x12PU13`\x0e@p\b\xce\xc1\r\x03\xdc\xe0\x10\xe0\x10\xdcQ\xc3\x10\xf7\xf4#\x97b&+\x13$n\xda\xc1\u017c\x13g$\x9d\xb3\v\x89lA\x93\xc2\xfd\x1cv\xb6nj\xb6s\xfdP\xdc\xe1\xf6\x96\x99\x15q\xb3\x19\xb4w\xba\x12\x85\xd7\xfc*\x97\x02\x82epe\x16\x17-\x1fx\xe1\x14\x88\x89\xdd\xe9I\x19\x98$\x81\xc9\x12\b\x03\xb4\xc9$\xa6.\xf5c|\xf0\x1c\u2f671\xda{\x06\xa3\xe7N#~\xfe,\x92\x89\xf8\fi\xa7\xbd\x11\x13J\u034d\x81x\x89\xae\xdc\xc7\xc2\xf2$onnBCC\xe3\xe8\x92%\x8b\u007f\xfc\xd0C\x0f=\xa5\xaa^\x89\xe1\xe0`?jk\xeb\xfc\xc5\u40f9\x1f\xbf\xefx\xeb\xad\xffEK\xcb\xcd\xde\xccS\x88\xf0\v/<\xff\u0653'On\xeb\xec\xec\xbc\xfe\xddw\xdb0\x1a\x8fg}[\xbc\xa0nfw \x04\x82\xeb\xa0LFaU-\u0095\xb5P\nK\x10(*C\xa8<\x8aP$\x8a`Y\x15\xe4\x82b(\x05\xc5\b\x14\x96B-\x8e@-,\x01\x91\x95,gM\x00\x10av\xc8\b\xc1\u0351\xee\x9c[|\xbe\x93u\xe7v^\x12\xe25\xa1r+b\xf2\xa9i\xa8\x95\x1d\xdb*\x0f\x9bj!y\x8a\x94\xf9T3\xe6>F\xb2lR.yd\x83e\xd6\xf8\xca*\x80:\x1b\x94p\x9aw(1\x1b\xf8)\x01\xb3(-JMb\x852\n\x89\x02Z:\x83\x89K\x03&h\xf7\x9e\xc1X\xdfY\x8c\xf5wc|\xa0\v\x13\x17\a\xbd\xe3\xe0(3yn\xeb\x14E\x89W\xc6#\xf2\fN\x15B\x80R\x02YV \xcb\x12\x1a\x1b\x1b\xd1\xdc\xdc<\x19\x8dV\xb5\xdeu\u05dd?Y\xb5jM\x9b\xfb\xf1\xdf\xff\xfe\xe3\xb8\uffbf\xf7\x17\x90\x0f\xe6~|\x9c\xe2\x95W\xfe\v[\xb6\u070e#G\x0ea\u03de\xbd\xec\x81\a\x1e\xb4\x95/U/\xbf\xfc\u04a7\xdb\xdbO\xdc\xdd\xd6vt\xfd\xa9S\xa7\x90H$\x90N\xa7=L\xc5L\n\xc603hj\x82<\xa1\x14L\x0e@\t\x17A\n\x85!\a\v\xa0\x84\x8b\xa0\x16\x95!\x14\xa9B\xb8j.\xc2\x15s\x10(.\x85\x1a.B\xb0\xb8\x14\xc1\x92\n\xa8\x85%\x90\x14\x15\xce \x04\x0eC7\xcci\xedB\x98\x13L\x05\xcf\x0e\f\xa6\xd4\xd2e\u00dc\x96\x03 ;\xcb\xd46\xb0\u02b5\x9b\xe5B@\xe3f\xe1\xd5\x00\xcd\u038c\x13v\xeaL\b\xe0\x18E\xb9\xc6\u06f9\xac\xa9,>\u01b1\x036\v\xae\xb6\xbe\x9c\x11\xc7!\xd11,s\rL&f\x87'\x17\x02\f\xe6&\x96\x99\x9e\u0115\xe1~\x8c\xf5w\"~\xfe,F{;\x90\x18\xee\xc3\xc4\xf0\x00&b\x17\xc0u\x87\xf3\xa6L\u029aY\x99\xb2A\xe21\xf52\x9d\x10E\xdeF,\xc30\x04!\x84\x84B!\x14\x15\x15\xa2\xb1\xb1\x11\x8b\x17/>\xdd\xd4\xd4\xf8\x8b\xa5K\x97\xbd|\xd3M-\xed\x00\xb0~\xfd\x8dt\u06f6m\xb8\xf3\xce\xcf\xf9\xea\x14\x1f\xcc\xfd\xf8C\x897\xde\xf8\x05}\xe3\x8d_\xb2'\x9ex\xd2V\xbe\x84\u007f\xf0\x83\x176\x1f=\xfa\u07b7\x8e\x1e=\xba``\xa0\x1f\xb1X\f\x9a\xa6\xe7-\t\xba\xb3C\x13\xe0I6\xd3\x16\x82Cp\xd7\fw\xca \xa9A\u0221\x02H\x81\x10\xa4@\x10J\xa8\x00\x81\xc2R\x84\"QD\x1a\xe6#\u04bc\b\xa5\x9f\xb8\x16\xe1\xb2JH\x8a\nI\x92 I\xd41\xc4\x12\x00\xb8\x01\xce-B\xc5eW\xeb1\x8d\xb2\xa8\ffm2 \x04\x19\x01d,x\xd2\f\xb3\u02d2[\x1bD\x96\n\xe2\xe6\tA\b\x93'2\xbf\xaf\xc3\xd0\xd20\xd2Ip]\x03\xd7M\x1baSJHA\xc0A\xb9n\x82\xb3\xaeA\x18:\fC\x03\x84\x80\x1a.\x82\x1c\f\x9b\x12A-\x8d\xf4D\x02S\xf1\u02d8\x8c\rc\xf2R?\x12\xc3\x03\x98\x8a_F*1\x8aTb\x14\xc9\u0118\xc7\a\x85P\x06\xcaXv\x93\xb3\v\x964\x0f\x80C\x88\x19\x12Ka\x06!\x04(**FYY\x19\xae\xb9\xa6\x19MM\x8d?^\xb0`\xc1\xd3\u06f6}\xb9\x8b\x102l?]k\xeb\xd3l\xeb\xd6m\xba\xbf2|0\xf7\xe3\x0f4\xac\x81\xd1n\xe5\vmm\xdd\xf1\xb9\u00c7\x0f\xdf\u007f\xf2\xe4\xff-\x1d\x19\x19\xc1\xf0\xf002\x99\x8cA\b\x04\xa5L2\v\x9d9\x1c\xac\xc5E\x10\xf7\x84g\xe7\xa2\x10\xdc\u021b5RJ!\a\xc3P\vK\x10,-G\u025cFT^\xb3\b%\xd1Z\x14\x94U \\Z\t\xb5\xa8\x14r(\fI\t\x80I2d\x89\x81P\xe6iN2\v\x8f\xe6f\xc2u\x03\x84\xeb\xa0\xe0\xa000\x9d\xd611\x95\xc4\u0115+\x98\x1a\x1f\xc5\xd4D\x02\xa9\xa9\t\xe8\xa9$2\xa9\x142\x13cHO\x8cCKN\xc2H\xa7`d\xd204\xf3\xab\x9eJ\x82k\xa6m\xb00\f\ap\xadZ\x02\xb1\xc0\x94s\xdd,\x06\x1b\xe6\xfb\xb4\x1b\xb3\f-\x03\xaee\xa0k\x19\xf3\u07fav\xf5\x05\x9d\x95\x8bRPP\xc7\xc7\x1c\x8e\u01ce;\x03'\xc43(\x9b\xeb\xban\b\xc1%UUI$\x12AYY\x19\xae\xbdv~b\xf9\xf2\xa5\xaf/_\xbe\xec_n\xbe\xf93\x1e>\xfc\xb5\xd7^!\xd5\xd5Q\xb1j\xd5\x1a\u007f1\xf8`\xee\xc7\x1fC\xec\xdc\xf9sr\xeb\xad\u007f)r8\u04ed\x1d\x1d\x1d[\xbb\xba\xba\xe7\x0f\r\r\x05c\xb1\x18\xe2\xf11!\x047\b\xa1T\bn+(H.\x1d\xe3\xf6Gw\xeeT\xe2\x92\xf59\xed\xa3B\x18.\x87?\x02I\r\x80\xc92\x98\xacB\t\x86\xa0\x14\x14\xa1\xb8\xbc\x1a\x85\x955\b\x16\x95@\r\x86!)*\x18\xa5Y!$#\x020t\x18\x99\f2\xa9i\xd3\xe27\x93\x82\xd0\u04d8\xba2\x8exl\x04\x93\x93W\x90\x9e\x9e\x86\xaee\xc0\r\xdd\x04^+\x03\xe7\u0724v\x84\xf8\xe8Tw6\x10\x13W\xb3\x8eS\xb4\x9c\xc9w\xbb5\xdd\xc4\xf1+\x067\x83J\x12#\x95\x95\x95(++CAA\xc1x]]]\xfb\xfa\xf5\xeb\xf7\xdes\xcf\u059f\x12B:\xdc\xd7{\xf0\xc1\xaf\x93{\xef\xbdW\xd4\xd4\xd4\xfa7\xbf\x0f\xe6~\xfc\xb1\xc5\xd3O?\x85S\xa7N\x91'\x9ex\u049d\xa9\xb3C\x87\x0e\u073as\xe7\xceM\xdd\xdd=\xab\x93\xc9\xe4\x82X,\x86\x81\x81ALOOA\xd7uLO'!\xc4\a2\xac\xbd\u028dLr\xe7\xd2\xffN\x16E\xde\xd1l\x1f\xe5\x02\xf5H\x02]_\xad\xb6y\u4302\xcb97y\u078d\xfd\x18\xce\x058\xe7\x90$\t\xaa\xaa\xa2\xa8\xa8\b\xd1h\x14\x91H\xd9XII\xc9\u19a6\xa6C\x1b7\xb6\x1c\xfd\u0527n\xdaE\b\xf1\xf0\xdfo\xbe\xf9+|\xfa\xd3\u007f\xe1\xdf\xec>\x98\xfb\xf1\xa7\x10\xb3\xa9\x18\x84\xe0\u05fe\xf8\xe2\x8b\x1b\xdb\xdb\xdb\xe7\uaeb6\xe2\u0295\x89\xf2s\xe7\xceM\x85\xc3\xe1\u56a6\x05{{\xcf#\x91H \x93I\xc308\f\xc3@&\x93\xf9\xe3\\x\xb9Y2!\x1e\xd31\xea\xea\xc1w\x17D\xed,;\xdf\x06f\u007f\x9fR{\xec\x9a\xd5\xf9i\xa9V\x18cP\xd5\x00**\xcaQ[[\v\u01a4\xe9`0\xd0Y]]=\xac\xaa\u02ae\xdbn\xdbrv\u035a\xb5\xbf\xca\u05d5\xb9k\xd7/q\xcb-\x9f\xf1on\x1f\xcc\xfd\xf8S\x8c\x8b\x17\x87\x10\x8d\xd6`p\xb0\x8f\xd4\xd6~B\xe4\x1c\xfb\xab\x00\x94\xec\u077b7\xd9\xdb\xdb3\xaf\xaf\xaf\xaf\xee\u0295\xc9\x06Bp\xfd\xc8H\x8c\xc6b#)JiEQQ\u046a\u02d7/c``\x10\x13\x13\x13\u0434\f4M\x83\xae\x1b0\f\x03\x9a\xa6\x9b<\xb7\xdbf\xe0Cj\x11\xbf\x9a\x89\x96\x99\a\x93\x19\xf3D\xb3\t\xf2l\xb9\xbdG\xcbm6\xfa0f\xd3%\xc4j\xa42m\x87\u0740\x9d}FB!\u02e6R\xc56\xb7b\x8cZ\xe0mf\u07a5\xa5%\xa8\xad\xadE0\x18\ucaac\xac\xec\f\x04\x82GC\xa1`\xe7\xbcy\u05cc\xb6\xb4l8\x1f\x8d\xd6^$\x84$\xec\xabvv\x9e!\xf3\xe6\xcd\x17\x00\xb0c\xc7S\u063cy3\xe6\u0319\xeb\xdf\xd0>\x98\xfb\xf1\xa7\x1c==\x9dhj\x9a\a!\x04v\xee\xfc9\x89\xc5b\xec\xc9'\x9f\x16\u01cf\x1f7\xf2\x03\xa6\xa8\xb0\xeeK\xbd\xa3\xe3T\xb8\xaf\xaf\u007fN[[[@Q\xe4\u0159Lf\xe5\u0673g\xb5\xe9\xe9diII\xf1\xba\x8b\x17/\x97\x0e\f\f`||\x1c\xba\xae!\x93\u0450L\x9a\xfe/6\xd8Sk B~\x80\xcf\xef\xed2\xc3\xfb\u071e=*\x04r\x8b\xb7\xb2,\x83Rj6\xf5\xe4\\U\x96e(\xb2\x9c\x05W\x0f\xc7M\x88w,\x9b\x95=\xeb\xba\x0e\u039d\xe9M\xf6\xefp\xceA)\x81$IP\x14\x15\xb2,\x811\x06J\x19dYBqq\xb1\x88F\xa3\xa4\xa4\xa48\x1d\x8f\xc7\x0f\x8c\x8d\x8d\x9fkhh\b44\u05031\xe98\xe7\xbcm\xf5\xea\x1b\x86W\xaf^;F\b\x19\xcd}\xcf\xf7\xddw\xaf\xb4b\xc5r\xb1l\xd92\xbed\xc9R\xf1\xfa\xeb\xff\x83M\x9b6\xfb7\xb0\x0f\xe6~\xf8q\xb5\fW\xe0\u0529\xf7iOO/y\xfb\xed}(,\f\xd3D\xe2\n}\xfc\xf1\xffH\xff\x86\xdf\v\x02\x10\xa9\u0514|\xe4\xc8\xe1\u04b6\xb6cJEE\xf9\xc6\u04e7;\x16\x8d\x8c\u0116\x8d\x8c\x8c.\x1e\x1a\x1a\n'\x12\xe3\xd40\xcc\x06\x97t:\x8d\xa9\xa9)LM%a\xaa\r\t4M\x87\xa6i\xf9\x93k\x80\u0232\fIbfc\x90p\x18sU\r\xa0\xa8\xa8\x10\x81@\x00\x92$\x81s\x81d2\t\x00P\x14\x05\xc1`\x00\x92$\x9bEXY\x02!$I\b\x9d,..DAA\x01\x14EE \xa0BUU(\x8a\n\xc6X\xf6\x89UU\xa5\xc1`\x80^\xb80\xb4\xef\xfc\xf9\xf3\xa7\xc2\xe10K\xa7\u04d9\xea\xea\xea\xe6`0T\xdc\xdf\xdfwZQ\x14c\xee\u0739\xa4\xbe\xbe\x1e\xc5\xc5E\b\x85B2\x80\xc1c\xc7\xda\xf7LNN&\u05acY%777\x19\r\r\r\xf1H\xa4j\n\xf6\xdc\rB\x92\xb9\xeb~\xfb\xf6\x87\x95T*i\xd4\xd7\u05cbU\xabV\x8a\xa5KW\xf8\xdap\x1f\xcc\xfd\xf0\xe3\u00cb\xae\xae3\u063d\xfb\xd7\u0636\xedK\xbf\xf1\xb1\xd3\xd3\x13U\x87\x0e\x1d^\xb7\u007f\xff\x81\x8at:\xb5H\b\\\xdf\xdd\u074dX,\x16\xe6\\\xd4%\x12\t#\x95JrEQKB\xa1\x90\xb5A8\x14\rc\fB\b$\x12\t=\x9dNO\xaa\xaaJM\xe0UH(\x14\x82,+\xa3\x15\x15\x91X}}=\"\x91\x88$\x84\xb8\xd4\xd9\xd9\xf9v:\x9d\x89777K\xcd\xcdM(..\x86$1Z\\\\\"\x15\x16\x16\xbe;22z|\xf5\xea\x1b\fYV\xd9\aY\x8b\x84\x90\xe9<\x9b\x98G\xfe\xf9\xdb\xc4\x0f\u007f\xf8\"\u05ad[\x87\xba\xba\x06\xff\xa6\xf2\xc1\xdc\x0f?>>\xd1\xde\xfe\x1e\x1e}\xf4\x9f\xf1\uaaef}\x90\x93@\xf9\x89\x13\xc7n>r\xa4-}\xe6\xcc\x19\xbd\xb2\xb2\xe2\x9a\xf2\xf2\xc8\n\xceE\x88s\xce3\x99\x8c\xb0\xb2c\x99\x10\x92\xec\xed=\xff\xf6\x85\v\x17\x06\xeb\xea\xeaX]]\x1d\xa9\xae\x8e\xb2\x86\x86\x06\xb2h\u0452vB\xc8\u064f\xe3\xe71<<\x88\xeaj_&\xe8\x87\x1f~\xfc\x81\xc7\xd0\xd0\xc0\xc7\xf2u\xdd\u007f\xff\xdfbp\xb0\xdf\xff\x03\xf9\xf1\xa1\xc7\xff\x03 \x99\rH\xa6\x0e6\x92\x00\x00\x00\x00IEND\xaeB`\x82") + +// dashboardDockerfile is the Dockerfile required to build an dashboard container +// to aggregate various private network services under one easily accessible page. +var dashboardDockerfile = ` +FROM mhart/alpine-node:latest + +RUN \ + npm install connect serve-static && \ + \ + echo 'var connect = require("connect");' > server.js && \ + echo 'var serveStatic = require("serve-static");' >> server.js && \ + echo 'connect().use(serveStatic("/dashboard")).listen(80, function(){' >> server.js && \ + echo ' console.log("Server running on 80...");' >> server.js && \ + echo '});' >> server.js + +ADD {{.Network}}.json /dashboard/{{.Network}}.json +ADD index.html /dashboard/index.html +ADD puppeth.png /dashboard/puppeth.png + +EXPOSE 80 + +CMD ["node", "/server.js"] +` + +// dashboardComposefile is the docker-compose.yml file required to deploy and +// maintain an service aggregating dashboard. +var dashboardComposefile = ` +version: '2' +services: + dashboard: + build: . + image: {{.Network}}/dashboard{{if not .VHost}} + ports: + - "{{.Port}}:80"{{else}} + environment: + - VIRTUAL_HOST={{.VHost}}{{end}} + restart: always +` + +// deployDashboard deploys a new dashboard container to a remote machine via SSH, +// docker and docker-compose. If an instance with the specified network name +// already exists there, it will be overwritten! +func deployDashboard(client *sshClient, network string, port int, vhost string, services map[string]string, conf *config, ethstats bool) ([]byte, error) { + // Generate the content to upload to the server + workdir := fmt.Sprintf("%d", rand.Int63()) + files := make(map[string][]byte) + + dockerfile := new(bytes.Buffer) + template.Must(template.New("").Parse(dashboardDockerfile)).Execute(dockerfile, map[string]interface{}{ + "Network": network, + }) + files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() + + composefile := new(bytes.Buffer) + template.Must(template.New("").Parse(dashboardComposefile)).Execute(composefile, map[string]interface{}{ + "Network": network, + "Port": port, + "VHost": vhost, + }) + files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() + + statsLogin := fmt.Sprintf("yournode:%s", conf.ethstats) + if !ethstats { + statsLogin = "" + } + indexfile := new(bytes.Buffer) + template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{ + "Network": network, + "NetworkID": conf.genesis.Config.ChainId, + "NetworkTitle": strings.Title(network), + "EthstatsPage": services["ethstats"], + "ExplorerPage": services["explorer"], + "WalletPage": services["wallet"], + "FaucetPage": services["faucet"], + "GethGenesis": network + ".json", + "BootnodesFull": conf.bootFull, + "BootnodesLight": conf.bootLight, + "BootnodesFullFlat": strings.Join(conf.bootFull, ","), + "BootnodesLightFlat": strings.Join(conf.bootLight, ","), + "Ethstats": statsLogin, + }) + files[filepath.Join(workdir, "index.html")] = indexfile.Bytes() + + genesis, _ := conf.genesis.MarshalJSON() + files[filepath.Join(workdir, network+".json")] = genesis + + files[filepath.Join(workdir, "puppeth.png")] = dashboardMascot + + // Upload the deployment files to the remote server (and clean up afterwards) + if out, err := client.Upload(files); err != nil { + return out, err + } + defer client.Run("rm -rf " + workdir) + + // Build and deploy the dashboard service + return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network)) +} + +// dashboardInfos is returned from an dashboard status check to allow reporting +// various configuration parameters. +type dashboardInfos struct { + host string + port int +} + +// String implements the stringer interface. +func (info *dashboardInfos) String() string { + return fmt.Sprintf("host=%s, port=%d", info.host, info.port) +} + +// checkDashboard does a health-check against a dashboard container to verify if +// it's running, and if yes, gathering a collection of useful infos about it. +func checkDashboard(client *sshClient, network string) (*dashboardInfos, error) { + // Inspect a possible ethstats container on the host + infos, err := inspectContainer(client, fmt.Sprintf("%s_dashboard_1", network)) + if err != nil { + return nil, err + } + if !infos.running { + return nil, ErrServiceOffline + } + // Resolve the port from the host, or the reverse proxy + port := infos.portmap["80/tcp"] + if port == 0 { + if proxy, _ := checkNginx(client, network); proxy != nil { + port = proxy.port + } + } + if port == 0 { + return nil, ErrNotExposed + } + // Resolve the host from the reverse-proxy and configure the connection string + host := infos.envvars["VIRTUAL_HOST"] + if host == "" { + host = client.server + } + // Run a sanity check to see if the port is reachable + if err = checkPort(host, port); err != nil { + log.Warn("Dashboard service seems unreachable", "server", host, "port", port, "err", err) + } + // Container available, assemble and return the useful infos + return &dashboardInfos{ + host: host, + port: port, + }, nil +} diff --git a/cmd/puppeth/module_ethstats.go b/cmd/puppeth/module_ethstats.go new file mode 100644 index 000000000..571df1454 --- /dev/null +++ b/cmd/puppeth/module_ethstats.go @@ -0,0 +1,159 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bytes" + "fmt" + "math/rand" + "path/filepath" + "strings" + "text/template" + + "github.com/ethereum/go-ethereum/log" +) + +// ethstatsDockerfile is the Dockerfile required to build an ethstats backend +// and associated monitoring site. +var ethstatsDockerfile = ` +FROM mhart/alpine-node:latest + +RUN \ + apk add --update git && \ + git clone --depth=1 https://github.com/karalabe/eth-netstats && \ + apk del git && rm -rf /var/cache/apk/* && \ + \ + cd /eth-netstats && npm install && npm install -g grunt-cli && grunt + +WORKDIR /eth-netstats +EXPOSE 3000 + +RUN echo 'module.exports = {trusted: [{{.Trusted}}], banned: []};' > lib/utils/config.js + +CMD ["npm", "start"] +` + +// ethstatsComposefile is the docker-compose.yml file required to deploy and +// maintain an ethstats monitoring site. +var ethstatsComposefile = ` +version: '2' +services: + ethstats: + build: . + image: {{.Network}}/ethstats{{if not .VHost}} + ports: + - "{{.Port}}:3000"{{end}} + environment: + - WS_SECRET={{.Secret}}{{if .VHost}} + - VIRTUAL_HOST={{.VHost}}{{end}} + restart: always +` + +// deployEthstats deploys a new ethstats container to a remote machine via SSH, +// docker and docker-compose. If an instance with the specified network name +// already exists there, it will be overwritten! +func deployEthstats(client *sshClient, network string, port int, secret string, vhost string, trusted []string) ([]byte, error) { + // Generate the content to upload to the server + workdir := fmt.Sprintf("%d", rand.Int63()) + files := make(map[string][]byte) + + for i, address := range trusted { + trusted[i] = fmt.Sprintf("\"%s\"", address) + } + + dockerfile := new(bytes.Buffer) + template.Must(template.New("").Parse(ethstatsDockerfile)).Execute(dockerfile, map[string]interface{}{ + "Trusted": strings.Join(trusted, ", "), + }) + files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() + + composefile := new(bytes.Buffer) + template.Must(template.New("").Parse(ethstatsComposefile)).Execute(composefile, map[string]interface{}{ + "Network": network, + "Port": port, + "Secret": secret, + "VHost": vhost, + }) + files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() + + // Upload the deployment files to the remote server (and clean up afterwards) + if out, err := client.Upload(files); err != nil { + return out, err + } + defer client.Run("rm -rf " + workdir) + + // Build and deploy the ethstats service + return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network)) +} + +// ethstatsInfos is returned from an ethstats status check to allow reporting +// various configuration parameters. +type ethstatsInfos struct { + host string + port int + secret string + config string +} + +// String implements the stringer interface. +func (info *ethstatsInfos) String() string { + return fmt.Sprintf("host=%s, port=%d, secret=%s", info.host, info.port, info.secret) +} + +// checkEthstats does a health-check against an ethstats server to verify whether +// it's running, and if yes, gathering a collection of useful infos about it. +func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) { + // Inspect a possible ethstats container on the host + infos, err := inspectContainer(client, fmt.Sprintf("%s_ethstats_1", network)) + if err != nil { + return nil, err + } + if !infos.running { + return nil, ErrServiceOffline + } + // Resolve the port from the host, or the reverse proxy + port := infos.portmap["3000/tcp"] + if port == 0 { + if proxy, _ := checkNginx(client, network); proxy != nil { + port = proxy.port + } + } + if port == 0 { + return nil, ErrNotExposed + } + // Resolve the host from the reverse-proxy and configure the connection string + host := infos.envvars["VIRTUAL_HOST"] + if host == "" { + host = client.server + } + secret := infos.envvars["WS_SECRET"] + config := fmt.Sprintf("%s@%s", secret, host) + if port != 80 && port != 443 { + config += fmt.Sprintf(":%d", port) + } + // Run a sanity check to see if the port is reachable + if err = checkPort(host, port); err != nil { + log.Warn("Ethstats service seems unreachable", "server", host, "port", port, "err", err) + } + // Container available, assemble and return the useful infos + return ðstatsInfos{ + host: host, + port: port, + secret: secret, + config: config, + }, nil +} diff --git a/cmd/puppeth/module_faucet.go b/cmd/puppeth/module_faucet.go new file mode 100644 index 000000000..44016c53e --- /dev/null +++ b/cmd/puppeth/module_faucet.go @@ -0,0 +1,210 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bytes" + "fmt" + "html/template" + "math/rand" + "path/filepath" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/log" +) + +// faucetDockerfile is the Dockerfile required to build an faucet container to +// grant crypto tokens based on GitHub authentications. +var faucetDockerfile = ` +FROM alpine:latest + +RUN mkdir /go +ENV GOPATH /go + +RUN \ + apk add --update git go make gcc musl-dev ca-certificates linux-headers && \ + mkdir -p $GOPATH/src/github.com/ethereum && \ + (cd $GOPATH/src/github.com/ethereum && git clone --depth=1 https://github.com/ethereum/go-ethereum) && \ + go build -v github.com/ethereum/go-ethereum/cmd/faucet && \ + apk del git go make gcc musl-dev linux-headers && \ + rm -rf $GOPATH && rm -rf /var/cache/apk/* + +ADD genesis.json /genesis.json +ADD account.json /account.json +ADD account.pass /account.pass + +EXPOSE 8080 + +CMD [ \ + "/faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", \ + "--ethport", "{{.EthPort}}", "--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", \ + "--github.user", "{{.GitHubUser}}", "--github.token", "{{.GitHubToken}}", "--account.json", "/account.json", "--account.pass", "/account.pass" \ +]` + +// faucetComposefile is the docker-compose.yml file required to deploy and maintain +// a crypto faucet. +var faucetComposefile = ` +version: '2' +services: + faucet: + build: . + image: {{.Network}}/faucet + ports: + - "{{.EthPort}}:{{.EthPort}}"{{if not .VHost}} + - "{{.ApiPort}}:8080"{{end}} + volumes: + - {{.Datadir}}:/root/.faucet + environment: + - ETH_PORT={{.EthPort}} + - ETH_NAME={{.EthName}} + - FAUCET_AMOUNT={{.FaucetAmount}} + - FAUCET_MINUTES={{.FaucetMinutes}} + - GITHUB_USER={{.GitHubUser}} + - GITHUB_TOKEN={{.GitHubToken}}{{if .VHost}} + - VIRTUAL_HOST={{.VHost}} + - VIRTUAL_PORT=8080{{end}} + restart: always +` + +// deployFaucet deploys a new faucet container to a remote machine via SSH, +// docker and docker-compose. If an instance with the specified network name +// already exists there, it will be overwritten! +func deployFaucet(client *sshClient, network string, bootnodes []string, config *faucetInfos) ([]byte, error) { + // Generate the content to upload to the server + workdir := fmt.Sprintf("%d", rand.Int63()) + files := make(map[string][]byte) + + dockerfile := new(bytes.Buffer) + template.Must(template.New("").Parse(faucetDockerfile)).Execute(dockerfile, map[string]interface{}{ + "NetworkID": config.node.network, + "Bootnodes": strings.Join(bootnodes, ","), + "Ethstats": config.node.ethstats, + "EthPort": config.node.portFull, + "GitHubUser": config.githubUser, + "GitHubToken": config.githubToken, + "FaucetName": strings.Title(network), + "FaucetAmount": config.amount, + "FaucetMinutes": config.minutes, + }) + files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() + + composefile := new(bytes.Buffer) + template.Must(template.New("").Parse(faucetComposefile)).Execute(composefile, map[string]interface{}{ + "Network": network, + "Datadir": config.node.datadir, + "VHost": config.host, + "ApiPort": config.port, + "EthPort": config.node.portFull, + "EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")], + "GitHubUser": config.githubUser, + "GitHubToken": config.githubToken, + "FaucetAmount": config.amount, + "FaucetMinutes": config.minutes, + }) + files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() + + files[filepath.Join(workdir, "genesis.json")] = []byte(config.node.genesis) + files[filepath.Join(workdir, "account.json")] = []byte(config.node.keyJSON) + files[filepath.Join(workdir, "account.pass")] = []byte(config.node.keyPass) + + // Upload the deployment files to the remote server (and clean up afterwards) + if out, err := client.Upload(files); err != nil { + return out, err + } + defer client.Run("rm -rf " + workdir) + + // Build and deploy the faucet service + return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network)) +} + +// faucetInfos is returned from an faucet status check to allow reporting various +// configuration parameters. +type faucetInfos struct { + node *nodeInfos + host string + port int + amount int + minutes int + githubUser string + githubToken string +} + +// String implements the stringer interface. +func (info *faucetInfos) String() string { + return fmt.Sprintf("host=%s, api=%d, eth=%d, amount=%d, minutes=%d, github=%s, ethstats=%s", info.host, info.port, info.node.portFull, info.amount, info.minutes, info.githubUser, info.node.ethstats) +} + +// checkFaucet does a health-check against an faucet server to verify whether +// it's running, and if yes, gathering a collection of useful infos about it. +func checkFaucet(client *sshClient, network string) (*faucetInfos, error) { + // Inspect a possible faucet container on the host + infos, err := inspectContainer(client, fmt.Sprintf("%s_faucet_1", network)) + if err != nil { + return nil, err + } + if !infos.running { + return nil, ErrServiceOffline + } + // Resolve the port from the host, or the reverse proxy + port := infos.portmap["8080/tcp"] + if port == 0 { + if proxy, _ := checkNginx(client, network); proxy != nil { + port = proxy.port + } + } + if port == 0 { + return nil, ErrNotExposed + } + // Resolve the host from the reverse-proxy and the config values + host := infos.envvars["VIRTUAL_HOST"] + if host == "" { + host = client.server + } + amount, _ := strconv.Atoi(infos.envvars["FAUCET_AMOUNT"]) + minutes, _ := strconv.Atoi(infos.envvars["FAUCET_MINUTES"]) + + // Retrieve the funding account informations + var out []byte + keyJSON, keyPass := "", "" + if out, err = client.Run(fmt.Sprintf("docker exec %s_faucet_1 cat /account.json", network)); err == nil { + keyJSON = string(bytes.TrimSpace(out)) + } + if out, err = client.Run(fmt.Sprintf("docker exec %s_faucet_1 cat /account.pass", network)); err == nil { + keyPass = string(bytes.TrimSpace(out)) + } + // Run a sanity check to see if the port is reachable + if err = checkPort(host, port); err != nil { + log.Warn("Faucet service seems unreachable", "server", host, "port", port, "err", err) + } + // Container available, assemble and return the useful infos + return &faucetInfos{ + node: &nodeInfos{ + datadir: infos.volumes["/root/.faucet"], + portFull: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"], + ethstats: infos.envvars["ETH_NAME"], + keyJSON: keyJSON, + keyPass: keyPass, + }, + host: host, + port: port, + amount: amount, + minutes: minutes, + githubUser: infos.envvars["GITHUB_USER"], + githubToken: infos.envvars["GITHUB_TOKEN"], + }, nil +} diff --git a/cmd/puppeth/module_nginx.go b/cmd/puppeth/module_nginx.go new file mode 100644 index 000000000..0eac5ace5 --- /dev/null +++ b/cmd/puppeth/module_nginx.go @@ -0,0 +1,106 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bytes" + "fmt" + "html/template" + "math/rand" + "path/filepath" + + "github.com/ethereum/go-ethereum/log" +) + +// nginxDockerfile is theis the Dockerfile required to build an nginx reverse- +// proxy. +var nginxDockerfile = `FROM jwilder/nginx-proxy` + +// nginxComposefile is the docker-compose.yml file required to deploy and maintain +// an nginx reverse-proxy. The proxy is responsible for exposing one or more HTTP +// services running on a single host. +var nginxComposefile = ` +version: '2' +services: + nginx: + build: . + image: {{.Network}}/nginx + ports: + - "{{.Port}}:80" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + restart: always +` + +// deployNginx deploys a new nginx reverse-proxy container to expose one or more +// HTTP services running on a single host. If an instance with the specified +// network name already exists there, it will be overwritten! +func deployNginx(client *sshClient, network string, port int) ([]byte, error) { + log.Info("Deploying nginx reverse-proxy", "server", client.server, "port", port) + + // Generate the content to upload to the server + workdir := fmt.Sprintf("%d", rand.Int63()) + files := make(map[string][]byte) + + dockerfile := new(bytes.Buffer) + template.Must(template.New("").Parse(nginxDockerfile)).Execute(dockerfile, nil) + files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() + + composefile := new(bytes.Buffer) + template.Must(template.New("").Parse(nginxComposefile)).Execute(composefile, map[string]interface{}{ + "Network": network, + "Port": port, + }) + files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() + + // Upload the deployment files to the remote server (and clean up afterwards) + if out, err := client.Upload(files); err != nil { + return out, err + } + defer client.Run("rm -rf " + workdir) + + // Build and deploy the ethstats service + return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network)) +} + +// nginxInfos is returned from an nginx reverse-proxy status check to allow +// reporting various configuration parameters. +type nginxInfos struct { + port int +} + +// String implements the stringer interface. +func (info *nginxInfos) String() string { + return fmt.Sprintf("port=%d", info.port) +} + +// checkNginx does a health-check against an nginx reverse-proxy to verify whether +// it's running, and if yes, gathering a collection of useful infos about it. +func checkNginx(client *sshClient, network string) (*nginxInfos, error) { + // Inspect a possible nginx container on the host + infos, err := inspectContainer(client, fmt.Sprintf("%s_nginx_1", network)) + if err != nil { + return nil, err + } + if !infos.running { + return nil, ErrServiceOffline + } + // Container available, assemble and return the useful infos + return &nginxInfos{ + port: infos.portmap["80/tcp"], + }, nil +} diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go new file mode 100644 index 000000000..78681934d --- /dev/null +++ b/cmd/puppeth/module_node.go @@ -0,0 +1,222 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bytes" + "fmt" + "math/rand" + "path/filepath" + "strconv" + "strings" + "text/template" + + "github.com/ethereum/go-ethereum/log" +) + +// nodeDockerfile is the Dockerfile required to run an Ethereum node. +var nodeDockerfile = ` +FROM ethereum/client-go:alpine-develop + +ADD genesis.json /genesis.json +{{if .Unlock}} + ADD signer.json /signer.json + ADD signer.pass /signer.pass +{{end}} +RUN \ + echo '/geth init /genesis.json' > geth.sh && \{{if .Unlock}} + echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}} + echo $'/geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine{{end}}{{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}}' >> geth.sh + +ENTRYPOINT ["/bin/sh", "geth.sh"] +` + +// nodeComposefile is the docker-compose.yml file required to deploy and maintain +// an Ethereum node (bootnode or miner for now). +var nodeComposefile = ` +version: '2' +services: + {{.Type}}: + build: . + image: {{.Network}}/{{.Type}} + ports: + - "{{.FullPort}}:{{.FullPort}}" + - "{{.FullPort}}:{{.FullPort}}/udp"{{if .Light}} + - "{{.LightPort}}:{{.LightPort}}/udp"{{end}} + volumes: + - {{.Datadir}}:/root/.ethereum + environment: + - FULL_PORT={{.FullPort}}/tcp + - LIGHT_PORT={{.LightPort}}/udp + - TOTAL_PEERS={{.TotalPeers}} + - LIGHT_PEERS={{.LightPeers}} + - STATS_NAME={{.Ethstats}} + - MINER_NAME={{.Etherbase}} + restart: always +` + +// deployNode deploys a new Ethereum node container to a remote machine via SSH, +// docker and docker-compose. If an instance with the specified network name +// already exists there, it will be overwritten! +func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos) ([]byte, error) { + kind := "sealnode" + if config.keyJSON == "" && config.etherbase == "" { + kind = "bootnode" + bootnodes = make([]string, 0) + } + // Generate the content to upload to the server + workdir := fmt.Sprintf("%d", rand.Int63()) + files := make(map[string][]byte) + + lightFlag := "" + if config.peersLight > 0 { + lightFlag = fmt.Sprintf("--lightpeers=%d --lightserv=50", config.peersLight) + } + dockerfile := new(bytes.Buffer) + template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{ + "NetworkID": config.network, + "Port": config.portFull, + "Peers": config.peersTotal, + "LightFlag": lightFlag, + "Bootnodes": strings.Join(bootnodes, ","), + "Ethstats": config.ethstats, + "Etherbase": config.etherbase, + "Unlock": config.keyJSON != "", + }) + files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() + + composefile := new(bytes.Buffer) + template.Must(template.New("").Parse(nodeComposefile)).Execute(composefile, map[string]interface{}{ + "Type": kind, + "Datadir": config.datadir, + "Network": network, + "FullPort": config.portFull, + "TotalPeers": config.peersTotal, + "Light": config.peersLight > 0, + "LightPort": config.portFull + 1, + "LightPeers": config.peersLight, + "Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")], + "Etherbase": config.etherbase, + }) + files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() + + //genesisfile, _ := json.MarshalIndent(config.genesis, "", " ") + files[filepath.Join(workdir, "genesis.json")] = []byte(config.genesis) + + if config.keyJSON != "" { + files[filepath.Join(workdir, "signer.json")] = []byte(config.keyJSON) + files[filepath.Join(workdir, "signer.pass")] = []byte(config.keyPass) + } + // Upload the deployment files to the remote server (and clean up afterwards) + if out, err := client.Upload(files); err != nil { + return out, err + } + defer client.Run("rm -rf " + workdir) + + // Build and deploy the bootnode service + return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network)) +} + +// nodeInfos is returned from a boot or seal node status check to allow reporting +// various configuration parameters. +type nodeInfos struct { + genesis []byte + network int64 + datadir string + ethstats string + portFull int + portLight int + enodeFull string + enodeLight string + peersTotal int + peersLight int + etherbase string + keyJSON string + keyPass string +} + +// String implements the stringer interface. +func (info *nodeInfos) String() string { + discv5 := "" + if info.peersLight > 0 { + discv5 = fmt.Sprintf(", portv5=%d", info.portLight) + } + return fmt.Sprintf("port=%d%s, datadir=%s, peers=%d, lights=%d, ethstats=%s", info.portFull, discv5, info.datadir, info.peersTotal, info.peersLight, info.ethstats) +} + +// checkNode does a health-check against an boot or seal node server to verify +// whether it's running, and if yes, whether it's responsive. +func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error) { + kind := "bootnode" + if !boot { + kind = "sealnode" + } + // Inspect a possible bootnode container on the host + infos, err := inspectContainer(client, fmt.Sprintf("%s_%s_1", network, kind)) + if err != nil { + return nil, err + } + if !infos.running { + return nil, ErrServiceOffline + } + // Resolve a few types from the environmental variables + totalPeers, _ := strconv.Atoi(infos.envvars["TOTAL_PEERS"]) + lightPeers, _ := strconv.Atoi(infos.envvars["LIGHT_PEERS"]) + + // Container available, retrieve its node ID and its genesis json + var out []byte + if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 /geth --exec admin.nodeInfo.id attach", network, kind)); err != nil { + return nil, ErrServiceUnreachable + } + id := bytes.Trim(bytes.TrimSpace(out), "\"") + + if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /genesis.json", network, kind)); err != nil { + return nil, ErrServiceUnreachable + } + genesis := bytes.TrimSpace(out) + + keyJSON, keyPass := "", "" + if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /signer.json", network, kind)); err == nil { + keyJSON = string(bytes.TrimSpace(out)) + } + if out, err = client.Run(fmt.Sprintf("docker exec %s_%s_1 cat /signer.pass", network, kind)); err == nil { + keyPass = string(bytes.TrimSpace(out)) + } + // Run a sanity check to see if the devp2p is reachable + port := infos.portmap[infos.envvars["FULL_PORT"]] + if err = checkPort(client.server, port); err != nil { + log.Warn(fmt.Sprintf("%s devp2p port seems unreachable", strings.Title(kind)), "server", client.server, "port", port, "err", err) + } + // Assemble and return the useful infos + stats := &nodeInfos{ + genesis: genesis, + datadir: infos.volumes["/root/.ethereum"], + portFull: infos.portmap[infos.envvars["FULL_PORT"]], + portLight: infos.portmap[infos.envvars["LIGHT_PORT"]], + peersTotal: totalPeers, + peersLight: lightPeers, + ethstats: infos.envvars["STATS_NAME"], + etherbase: infos.envvars["MINER_NAME"], + keyJSON: keyJSON, + keyPass: keyPass, + } + stats.enodeFull = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.portFull) + if stats.portLight != 0 { + stats.enodeLight = fmt.Sprintf("enode://%s@%s:%d?discport=%d", id, client.address, stats.portFull, stats.portLight) + } + return stats, nil +} diff --git a/cmd/puppeth/puppeth.go b/cmd/puppeth/puppeth.go new file mode 100644 index 000000000..f783a7981 --- /dev/null +++ b/cmd/puppeth/puppeth.go @@ -0,0 +1,55 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +// puppeth is a command to assemble and maintain private networks. +package main + +import ( + "math/rand" + "os" + "time" + + "github.com/ethereum/go-ethereum/log" + "gopkg.in/urfave/cli.v1" +) + +// main is just a boring entry point to set up the CLI app. +func main() { + app := cli.NewApp() + app.Name = "puppeth" + app.Usage = "assemble and maintain private Ethereum networks" + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "network", + Usage: "name of the network to administer", + }, + cli.IntFlag{ + Name: "loglevel", + Value: 4, + Usage: "log level to emit to the screen", + }, + } + app.Action = func(c *cli.Context) error { + // Set up the logger to print everything and the random generator + log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) + rand.Seed(time.Now().UnixNano()) + + // Start the wizard and relinquish control + makeWizard(c.String("network")).run() + return nil + } + app.Run(os.Args) +} diff --git a/cmd/puppeth/ssh.go b/cmd/puppeth/ssh.go new file mode 100644 index 000000000..34fbc566d --- /dev/null +++ b/cmd/puppeth/ssh.go @@ -0,0 +1,195 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "errors" + "fmt" + "io/ioutil" + "net" + "os" + "os/user" + "path/filepath" + "strings" + "syscall" + + "github.com/ethereum/go-ethereum/log" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/terminal" +) + +// sshClient is a small wrapper around Go's SSH client with a few utility methods +// implemented on top. +type sshClient struct { + server string // Server name or IP without port number + address string // IP address of the remote server + client *ssh.Client + logger log.Logger +} + +// dial establishes an SSH connection to a remote node using the current user and +// the user's configured private RSA key. +func dial(server string) (*sshClient, error) { + // Figure out a label for the server and a logger + label := server + if strings.Contains(label, ":") { + label = label[:strings.Index(label, ":")] + } + logger := log.New("server", label) + logger.Debug("Attempting to establish SSH connection") + + user, err := user.Current() + if err != nil { + return nil, err + } + // Configure the supported authentication methods (private key and password) + var auths []ssh.AuthMethod + + path := filepath.Join(user.HomeDir, ".ssh", "id_rsa") + if buf, err := ioutil.ReadFile(path); err != nil { + log.Warn("No SSH key, falling back to passwords", "path", path, "err", err) + } else { + key, err := ssh.ParsePrivateKey(buf) + if err != nil { + log.Warn("Bad SSH key, falling back to passwords", "path", path, "err", err) + } else { + auths = append(auths, ssh.PublicKeys(key)) + } + } + auths = append(auths, ssh.PasswordCallback(func() (string, error) { + fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", user.Username, server) + blob, err := terminal.ReadPassword(int(syscall.Stdin)) + + fmt.Println() + return string(blob), err + })) + // Resolve the IP address of the remote server + addr, err := net.LookupHost(label) + if err != nil { + return nil, err + } + if len(addr) == 0 { + return nil, errors.New("no IPs associated with domain") + } + // Try to dial in to the remote server + logger.Trace("Dialing remote SSH server", "user", user.Username, "key", path) + if !strings.Contains(server, ":") { + server += ":22" + } + client, err := ssh.Dial("tcp", server, &ssh.ClientConfig{User: user.Username, Auth: auths}) + if err != nil { + return nil, err + } + // Connection established, return our utility wrapper + c := &sshClient{ + server: label, + address: addr[0], + client: client, + logger: logger, + } + if err := c.init(); err != nil { + client.Close() + return nil, err + } + return c, nil +} + +// init runs some initialization commands on the remote server to ensure it's +// capable of acting as puppeth target. +func (client *sshClient) init() error { + client.logger.Debug("Verifying if docker is available") + if out, err := client.Run("docker version"); err != nil { + if len(out) == 0 { + return err + } + return fmt.Errorf("docker configured incorrectly: %s", out) + } + client.logger.Debug("Verifying if docker-compose is available") + if out, err := client.Run("docker-compose version"); err != nil { + if len(out) == 0 { + return err + } + return fmt.Errorf("docker-compose configured incorrectly: %s", out) + } + return nil +} + +// Close terminates the connection to an SSH server. +func (client *sshClient) Close() error { + return client.client.Close() +} + +// Run executes a command on the remote server and returns the combined output +// along with any error status. +func (client *sshClient) Run(cmd string) ([]byte, error) { + // Establish a single command session + session, err := client.client.NewSession() + if err != nil { + return nil, err + } + defer session.Close() + + // Execute the command and return any output + client.logger.Trace("Running command on remote server", "cmd", cmd) + return session.CombinedOutput(cmd) +} + +// Stream executes a command on the remote server and streams all outputs into +// the local stdout and stderr streams. +func (client *sshClient) Stream(cmd string) error { + // Establish a single command session + session, err := client.client.NewSession() + if err != nil { + return err + } + defer session.Close() + + session.Stdout = os.Stdout + session.Stderr = os.Stderr + + // Execute the command and return any output + client.logger.Trace("Streaming command on remote server", "cmd", cmd) + return session.Run(cmd) +} + +// Upload copied the set of files to a remote server via SCP, creating any non- +// existing folder in te mean time. +func (client *sshClient) Upload(files map[string][]byte) ([]byte, error) { + // Establish a single command session + session, err := client.client.NewSession() + if err != nil { + return nil, err + } + defer session.Close() + + // Create a goroutine that streams the SCP content + go func() { + out, _ := session.StdinPipe() + defer out.Close() + + for file, content := range files { + client.logger.Trace("Uploading file to server", "file", file, "bytes", len(content)) + + fmt.Fprintln(out, "D0755", 0, filepath.Dir(file)) // Ensure the folder exists + fmt.Fprintln(out, "C0644", len(content), filepath.Base(file)) // Create the actual file + out.Write(content) // Stream the data content + fmt.Fprint(out, "\x00") // Transfer end with \x00 + fmt.Fprintln(out, "E") // Leave directory (simpler) + } + }() + return session.CombinedOutput("/usr/bin/scp -v -tr ./") +} diff --git a/cmd/puppeth/wizard.go b/cmd/puppeth/wizard.go new file mode 100644 index 000000000..92d7962a0 --- /dev/null +++ b/cmd/puppeth/wizard.go @@ -0,0 +1,229 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "syscall" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/log" + "golang.org/x/crypto/ssh/terminal" +) + +// config contains all the configurations needed by puppeth that should be saved +// between sessions. +type config struct { + path string // File containing the configuration values + genesis *core.Genesis // Genesis block to cache for node deploys + bootFull []string // Bootnodes to always connect to by full nodes + bootLight []string // Bootnodes to always connect to by light nodes + ethstats string // Ethstats settings to cache for node deploys + + Servers []string `json:"servers,omitempty"` +} + +// flush dumps the contents of config to disk. +func (c config) flush() { + os.MkdirAll(filepath.Dir(c.path), 0755) + + sort.Strings(c.Servers) + out, _ := json.MarshalIndent(c, "", " ") + if err := ioutil.WriteFile(c.path, out, 0644); err != nil { + log.Warn("Failed to save puppeth configs", "file", c.path, "err", err) + } +} + +type wizard struct { + network string // Network name to manage + conf config // Configurations from previous runs + + servers map[string]*sshClient // SSH connections to servers to administer + services map[string][]string // Ethereum services known to be running on servers + + in *bufio.Reader // Wrapper around stdin to allow reading user input +} + +// read reads a single line from stdin, trimming if from spaces. +func (w *wizard) read() string { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + return strings.TrimSpace(text) +} + +// readString reads a single line from stdin, trimming if from spaces, enforcing +// non-emptyness. +func (w *wizard) readString() string { + for { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text != "" { + return text + } + } +} + +// readDefaultString reads a single line from stdin, trimming if from spaces. If +// an empty line is entered, the default value is returned. +func (w *wizard) readDefaultString(def string) string { + for { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text != "" { + return text + } + return def + } +} + +// readInt reads a single line from stdin, trimming if from spaces, enforcing it +// to parse into an integer. +func (w *wizard) readInt() int { + for { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + continue + } + val, err := strconv.Atoi(strings.TrimSpace(text)) + if err != nil { + log.Error("Invalid input, expected integer", "err", err) + continue + } + return val + } +} + +// readDefaultInt reads a single line from stdin, trimming if from spaces, enforcing +// it to parse into an integer. If an empty line is entered, the default value is +// returned. +func (w *wizard) readDefaultInt(def int) int { + for { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + return def + } + val, err := strconv.Atoi(strings.TrimSpace(text)) + if err != nil { + log.Error("Invalid input, expected integer", "err", err) + continue + } + return val + } +} + +// readPassword reads a single line from stdin, trimming it from the trailing new +// line and returns it. The input will not be echoed. +func (w *wizard) readPassword() string { + for { + fmt.Printf("> ") + text, err := terminal.ReadPassword(int(syscall.Stdin)) + if err != nil { + log.Crit("Failed to read password", "err", err) + } + fmt.Println() + return string(text) + } +} + +// readAddress reads a single line from stdin, trimming if from spaces and converts +// it to an Ethereum address. +func (w *wizard) readAddress() *common.Address { + for { + // Read the address from the user + fmt.Printf("> 0x") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + return nil + } + // Make sure it looks ok and return it if so + if len(text) != 40 { + log.Error("Invalid address length, please retry") + continue + } + bigaddr, _ := new(big.Int).SetString(text, 16) + address := common.BigToAddress(bigaddr) + return &address + } +} + +// readDefaultAddress reads a single line from stdin, trimming if from spaces and +// converts it to an Ethereum address. If an empty line is entered, the default +// value is returned. +func (w *wizard) readDefaultAddress(def common.Address) common.Address { + for { + // Read the address from the user + fmt.Printf("> 0x") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + return def + } + // Make sure it looks ok and return it if so + if len(text) != 40 { + log.Error("Invalid address length, please retry") + continue + } + bigaddr, _ := new(big.Int).SetString(text, 16) + return common.BigToAddress(bigaddr) + } +} + +// readJSON reads a raw JSON message and returns it. +func (w *wizard) readJSON() string { + var blob json.RawMessage + + for { + fmt.Printf("> ") + if err := json.NewDecoder(w.in).Decode(&blob); err != nil { + log.Error("Invalid JSON, please try again", "err", err) + continue + } + return string(blob) + } +} diff --git a/cmd/puppeth/wizard_dashboard.go b/cmd/puppeth/wizard_dashboard.go new file mode 100644 index 000000000..53a28a535 --- /dev/null +++ b/cmd/puppeth/wizard_dashboard.go @@ -0,0 +1,132 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/log" +) + +// deployDashboard queries the user for various input on deploying a web-service +// dashboard, after which is pushes the container. +func (w *wizard) deployDashboard() { + // Select the server to interact with + server := w.selectServer() + if server == "" { + return + } + client := w.servers[server] + + // Retrieve any active dashboard configurations from the server + infos, err := checkDashboard(client, w.network) + if err != nil { + infos = &dashboardInfos{ + port: 80, + host: client.server, + } + } + // Figure out which port to listen on + fmt.Println() + fmt.Printf("Which port should the dashboard listen on? (default = %d)\n", infos.port) + infos.port = w.readDefaultInt(infos.port) + + // Figure which virtual-host to deploy the dashboard on + infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host) + if err != nil { + log.Error("Failed to decide on dashboard host", "err", err) + return + } + // Port and proxy settings retrieved, figure out which services are available + available := make(map[string][]string) + for server, services := range w.services { + for _, service := range services { + available[service] = append(available[service], server) + } + } + listing := make(map[string]string) + for _, service := range []string{"ethstats", "explorer", "wallet", "faucet"} { + // Gather all the locally hosted pages of this type + var pages []string + for _, server := range available[service] { + client := w.servers[server] + if client == nil { + continue + } + // If there's a service running on the machine, retrieve it's port number + var port int + switch service { + case "ethstats": + if infos, err := checkEthstats(client, w.network); err == nil { + port = infos.port + } + case "faucet": + if infos, err := checkFaucet(client, w.network); err == nil { + port = infos.port + } + } + if page, err := resolve(client, w.network, service, port); err == nil && page != "" { + pages = append(pages, page) + } + } + // Promt the user to chose one, enter manually or simply not list this service + defLabel, defChoice := "don't list", len(pages)+2 + if len(pages) > 0 { + defLabel, defChoice = pages[0], 1 + } + fmt.Println() + fmt.Printf("Which %s service to list? (default = %s)\n", service, defLabel) + for i, page := range pages { + fmt.Printf(" %d. %s\n", i+1, page) + } + fmt.Printf(" %d. List external %s service\n", len(pages)+1, service) + fmt.Printf(" %d. Don't list any %s service\n", len(pages)+2, service) + + choice := w.readDefaultInt(defChoice) + if choice < 0 || choice > len(pages)+2 { + log.Error("Invalid listing choice, aborting") + return + } + switch { + case choice <= len(pages): + listing[service] = pages[choice-1] + case choice == len(pages)+1: + fmt.Println() + fmt.Printf("Which address is the external %s service at?\n", service) + listing[service] = w.readString() + default: + // No service hosting for this + } + } + // If we have ethstats running, ask whether to make the secret public or not + var ethstats bool + if w.conf.ethstats != "" { + fmt.Println() + fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)") + ethstats = w.readDefaultString("y") == "y" + } + // Try to deploy the dashboard container on the host + if out, err := deployDashboard(client, w.network, infos.port, infos.host, listing, &w.conf, ethstats); err != nil { + log.Error("Failed to deploy dashboard container", "err", err) + if len(out) > 0 { + fmt.Printf("%s\n", out) + } + return + } + // All ok, run a network scan to pick any changes up + w.networkStats(false) +} diff --git a/cmd/puppeth/wizard_ethstats.go b/cmd/puppeth/wizard_ethstats.go new file mode 100644 index 000000000..c117a6027 --- /dev/null +++ b/cmd/puppeth/wizard_ethstats.go @@ -0,0 +1,79 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/log" +) + +// deployEthstats queries the user for various input on deploying an ethstats +// monitoring server, after which it executes it. +func (w *wizard) deployEthstats() { + // Select the server to interact with + server := w.selectServer() + if server == "" { + return + } + client := w.servers[server] + + // Retrieve any active ethstats configurations from the server + infos, err := checkEthstats(client, w.network) + if err != nil { + infos = ðstatsInfos{ + port: 80, + host: client.server, + secret: "", + } + } + // Figure out which port to listen on + fmt.Println() + fmt.Printf("Which port should ethstats listen on? (default = %d)\n", infos.port) + infos.port = w.readDefaultInt(infos.port) + + // Figure which virtual-host to deploy ethstats on + if infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host); err != nil { + log.Error("Failed to decide on ethstats host", "err", err) + return + } + // Port and proxy settings retrieved, figure out the secret and boot ethstats + fmt.Println() + if infos.secret == "" { + fmt.Printf("What should be the secret password for the API? (must not be empty)\n") + infos.secret = w.readString() + } else { + fmt.Printf("What should be the secret password for the API? (default = %s)\n", infos.secret) + infos.secret = w.readDefaultString(infos.secret) + } + // Try to deploy the ethstats server on the host + trusted := make([]string, 0, len(w.servers)) + for _, client := range w.servers { + if client != nil { + trusted = append(trusted, client.address) + } + } + if out, err := deployEthstats(client, w.network, infos.port, infos.secret, infos.host, trusted); err != nil { + log.Error("Failed to deploy ethstats container", "err", err) + if len(out) > 0 { + fmt.Printf("%s\n", out) + } + return + } + // All ok, run a network scan to pick any changes up + w.networkStats(false) +} diff --git a/cmd/puppeth/wizard_faucet.go b/cmd/puppeth/wizard_faucet.go new file mode 100644 index 000000000..71d1c910b --- /dev/null +++ b/cmd/puppeth/wizard_faucet.go @@ -0,0 +1,172 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/log" +) + +// deployFaucet queries the user for various input on deploying a faucet, after +// which it executes it. +func (w *wizard) deployFaucet() { + // Select the server to interact with + server := w.selectServer() + if server == "" { + return + } + client := w.servers[server] + + // Retrieve any active faucet configurations from the server + infos, err := checkFaucet(client, w.network) + if err != nil { + infos = &faucetInfos{ + node: &nodeInfos{portFull: 30303, peersTotal: 25}, + port: 80, + host: client.server, + amount: 1, + minutes: 1440, + } + } + infos.node.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ") + infos.node.network = w.conf.genesis.Config.ChainId.Int64() + + // Figure out which port to listen on + fmt.Println() + fmt.Printf("Which port should the faucet listen on? (default = %d)\n", infos.port) + infos.port = w.readDefaultInt(infos.port) + + // Figure which virtual-host to deploy ethstats on + if infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host); err != nil { + log.Error("Failed to decide on faucet host", "err", err) + return + } + // Port and proxy settings retrieved, figure out the funcing amount per perdion configurations + fmt.Println() + fmt.Printf("How many Ethers to release per request? (default = %d)\n", infos.amount) + infos.amount = w.readDefaultInt(infos.amount) + + fmt.Println() + fmt.Printf("How many minutes to enforce between requests? (default = %d)\n", infos.minutes) + infos.minutes = w.readDefaultInt(infos.minutes) + + // Accessing GitHub gists requires API authorization, retrieve it + if infos.githubUser != "" { + fmt.Println() + fmt.Printf("Reused previous (%s) GitHub API authorization (y/n)? (default = yes)\n", infos.githubUser) + if w.readDefaultString("y") != "y" { + infos.githubUser, infos.githubToken = "", "" + } + } + if infos.githubUser == "" { + // No previous authorization (or new one requested) + fmt.Println() + fmt.Println("Which GitHub user to verify Gists through?") + infos.githubUser = w.readString() + + fmt.Println() + fmt.Println("What is the GitHub personal access token of the user? (won't be echoed)") + infos.githubToken = w.readPassword() + + // Do a sanity check query against github to ensure it's valid + req, _ := http.NewRequest("GET", "https://api.github.com/user", nil) + req.SetBasicAuth(infos.githubUser, infos.githubToken) + res, err := http.DefaultClient.Do(req) + if err != nil { + log.Error("Failed to verify GitHub authentication", "err", err) + return + } + defer res.Body.Close() + + var msg struct { + Login string `json:"login"` + Message string `json:"message"` + } + if err = json.NewDecoder(res.Body).Decode(&msg); err != nil { + log.Error("Failed to decode authorization response", "err", err) + return + } + if msg.Login != infos.githubUser { + log.Error("GitHub authorization failed", "user", infos.githubUser, "message", msg.Message) + return + } + } + // Figure out where the user wants to store the persistent data + fmt.Println() + if infos.node.datadir == "" { + fmt.Printf("Where should data be stored on the remote machine?\n") + infos.node.datadir = w.readString() + } else { + fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.node.datadir) + infos.node.datadir = w.readDefaultString(infos.node.datadir) + } + // Figure out which port to listen on + fmt.Println() + fmt.Printf("Which TCP/UDP port should the light client listen on? (default = %d)\n", infos.node.portFull) + infos.node.portFull = w.readDefaultInt(infos.node.portFull) + + // Set a proper name to report on the stats page + fmt.Println() + if infos.node.ethstats == "" { + fmt.Printf("What should the node be called on the stats page?\n") + infos.node.ethstats = w.readString() + ":" + w.conf.ethstats + } else { + fmt.Printf("What should the node be called on the stats page? (default = %s)\n", infos.node.ethstats) + infos.node.ethstats = w.readDefaultString(infos.node.ethstats) + ":" + w.conf.ethstats + } + // Load up the credential needed to release funds + if infos.node.keyJSON != "" { + var key keystore.Key + if err := json.Unmarshal([]byte(infos.node.keyJSON), &key); err != nil { + infos.node.keyJSON, infos.node.keyPass = "", "" + } else { + fmt.Println() + fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex()) + if w.readDefaultString("y") != "y" { + infos.node.keyJSON, infos.node.keyPass = "", "" + } + } + } + if infos.node.keyJSON == "" { + fmt.Println() + fmt.Println("Please paste the faucet's funding account key JSON:") + infos.node.keyJSON = w.readJSON() + + fmt.Println() + fmt.Println("What's the unlock password for the account? (won't be echoed)") + infos.node.keyPass = w.readPassword() + + if _, err := keystore.DecryptKey([]byte(infos.node.keyJSON), infos.node.keyPass); err != nil { + log.Error("Failed to decrypt key with given passphrase") + return + } + } + // Try to deploy the faucet server on the host + if out, err := deployFaucet(client, w.network, w.conf.bootLight, infos); err != nil { + log.Error("Failed to deploy faucet container", "err", err) + if len(out) > 0 { + fmt.Printf("%s\n", out) + } + return + } + // All ok, run a network scan to pick any changes up + w.networkStats(false) +} diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go new file mode 100644 index 000000000..a67812e92 --- /dev/null +++ b/cmd/puppeth/wizard_genesis.go @@ -0,0 +1,136 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bytes" + "fmt" + "math/big" + "math/rand" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" +) + +// makeGenesis creates a new genesis struct based on some user input. +func (w *wizard) makeGenesis() { + // Construct a default genesis block + genesis := &core.Genesis{ + Timestamp: uint64(time.Now().Unix()), + GasLimit: 4700000, + Difficulty: big.NewInt(1048576), + Alloc: make(core.GenesisAlloc), + Config: ¶ms.ChainConfig{ + HomesteadBlock: big.NewInt(1), + EIP150Block: big.NewInt(2), + EIP155Block: big.NewInt(3), + EIP158Block: big.NewInt(3), + }, + } + // Figure out which consensus engine to choose + fmt.Println() + fmt.Println("Which consensus engine to use? (default = clique)") + fmt.Println(" 1. Ethash - proof-of-work") + fmt.Println(" 2. Clique - proof-of-authority") + + choice := w.read() + switch { + case choice == "1": + // In case of ethash, we're pretty much done + genesis.Config.Ethash = new(params.EthashConfig) + genesis.ExtraData = make([]byte, 32) + + case choice == "" || choice == "2": + // In the case of clique, configure the consensus parameters + genesis.Difficulty = big.NewInt(1) + genesis.Config.Clique = ¶ms.CliqueConfig{ + Period: 15, + Epoch: 30000, + } + fmt.Println() + fmt.Println("How many seconds should blocks take? (default = 15)") + genesis.Config.Clique.Period = uint64(w.readDefaultInt(15)) + + // We also need the initial list of signers + fmt.Println() + fmt.Println("Which accounts are allowed to seal? (mandatory at least one)") + + var signers []common.Address + for { + if address := w.readAddress(); address != nil { + signers = append(signers, *address) + continue + } + if len(signers) > 0 { + break + } + } + // Sort the signers and embed into the extra-data section + for i := 0; i < len(signers); i++ { + for j := i + 1; j < len(signers); j++ { + if bytes.Compare(signers[i][:], signers[j][:]) > 0 { + signers[i], signers[j] = signers[j], signers[i] + } + } + } + genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65) + for i, signer := range signers { + copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:]) + } + + default: + log.Crit("Invalid consensus engine choice", "choice", choice) + } + // Consensus all set, just ask for initial funds and go + fmt.Println() + fmt.Println("Which accounts should be pre-funded? (advisable at least one)") + for { + // Read the address of the account to fund + if address := w.readAddress(); address != nil { + genesis.Alloc[*address] = core.GenesisAccount{ + Balance: new(big.Int).Lsh(big.NewInt(1), 256-7), // 2^256 / 128 (allow many pre-funds without balance overflows) + } + continue + } + break + } + // Add a batch of precompile balances to avoid them getting deleted + for i := int64(0); i < 256; i++ { + genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)} + } + fmt.Println() + + // Query the user for some custom extras + fmt.Println() + fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)") + genesis.Config.ChainId = big.NewInt(int64(w.readDefaultInt(rand.Intn(65536)))) + + fmt.Println() + fmt.Println("Anything fun to embed into the genesis block? (max 32 bytes)") + + extra := w.read() + if len(extra) > 32 { + extra = extra[:32] + } + genesis.ExtraData = append([]byte(extra), genesis.ExtraData[len(extra):]...) + + // All done, store the genesis and flush to disk + w.conf.genesis = genesis +} diff --git a/cmd/puppeth/wizard_intro.go b/cmd/puppeth/wizard_intro.go new file mode 100644 index 000000000..46383bb54 --- /dev/null +++ b/cmd/puppeth/wizard_intro.go @@ -0,0 +1,153 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/ethereum/go-ethereum/log" +) + +// makeWizard creates and returns a new puppeth wizard. +func makeWizard(network string) *wizard { + return &wizard{ + network: network, + servers: make(map[string]*sshClient), + services: make(map[string][]string), + in: bufio.NewReader(os.Stdin), + } +} + +// run displays some useful infos to the user, starting on the journey of +// setting up a new or managing an existing Ethereum private network. +func (w *wizard) run() { + fmt.Println("+-----------------------------------------------------------+") + fmt.Println("| Welcome to puppeth, your Ethereum private network manager |") + fmt.Println("| |") + fmt.Println("| This tool lets you create a new Ethereum network down to |") + fmt.Println("| the genesis block, bootnodes, miners and ethstats servers |") + fmt.Println("| without the hassle that it would normally entail. |") + fmt.Println("| |") + fmt.Println("| Puppeth uses SSH to dial in to remote servers, and builds |") + fmt.Println("| its network components out of Docker containers using the |") + fmt.Println("| docker-compose toolset. |") + fmt.Println("+-----------------------------------------------------------+") + fmt.Println() + + // Make sure we have a good network name to work with fmt.Println() + if w.network == "" { + fmt.Println("Please specify a network name to administer (no spaces, please)") + for { + w.network = w.readString() + if !strings.Contains(w.network, " ") { + fmt.Printf("Sweet, you can set this via --network=%s next time!\n\n", w.network) + break + } + log.Error("I also like to live dangerously, still no spaces") + } + } + log.Info("Administering Ethereum network", "name", w.network) + + // Load initial configurations and connect to all live servers + w.conf.path = filepath.Join(os.Getenv("HOME"), ".puppeth", w.network) + + blob, err := ioutil.ReadFile(w.conf.path) + if err != nil { + log.Warn("No previous configurations found", "path", w.conf.path) + } else if err := json.Unmarshal(blob, &w.conf); err != nil { + log.Crit("Previous configuration corrupted", "path", w.conf.path, "err", err) + } else { + for _, server := range w.conf.Servers { + log.Info("Dialing previously configured server", "server", server) + client, err := dial(server) + if err != nil { + log.Error("Previous server unreachable", "server", server, "err", err) + } + w.servers[server] = client + } + w.networkStats(false) + } + // Basics done, loop ad infinitum about what to do + for { + fmt.Println() + fmt.Println("What would you like to do? (default = stats)") + fmt.Println(" 1. Show network stats") + if w.conf.genesis == nil { + fmt.Println(" 2. Configure new genesis") + } else { + fmt.Println(" 2. Save existing genesis") + } + if len(w.servers) == 0 { + fmt.Println(" 3. Track new remote server") + } else { + fmt.Println(" 3. Manage tracked machines") + } + if len(w.services) == 0 { + fmt.Println(" 4. Deploy network components") + } else { + fmt.Println(" 4. Manage network components") + } + //fmt.Println(" 5. ProTips for common usecases") + + choice := w.read() + switch { + case choice == "" || choice == "1": + w.networkStats(false) + + case choice == "2": + // If we don't have a genesis, make one + if w.conf.genesis == nil { + w.makeGenesis() + } else { + // Otherwise just save whatever we currently have + fmt.Println() + fmt.Printf("Which file to save the genesis into? (default = %s.json)\n", w.network) + out, _ := json.MarshalIndent(w.conf.genesis, "", " ") + if err := ioutil.WriteFile(w.readDefaultString(fmt.Sprintf("%s.json", w.network)), out, 0644); err != nil { + log.Error("Failed to save genesis file", "err", err) + } + log.Info("Exported existing genesis block") + } + case choice == "3": + if len(w.servers) == 0 { + if w.makeServer() != "" { + w.networkStats(false) + } + } else { + w.manageServers() + } + case choice == "4": + if len(w.services) == 0 { + w.deployComponent() + } else { + w.manageComponents() + } + + case choice == "5": + w.networkStats(true) + + default: + log.Error("That's not something I can do") + } + } +} diff --git a/cmd/puppeth/wizard_netstats.go b/cmd/puppeth/wizard_netstats.go new file mode 100644 index 000000000..c2a933a55 --- /dev/null +++ b/cmd/puppeth/wizard_netstats.go @@ -0,0 +1,235 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/log" + "github.com/olekukonko/tablewriter" +) + +// networkStats verifies the status of network components and generates a protip +// configuration set to give users hints on how to do various tasks. +func (w *wizard) networkStats(tips bool) { + if len(w.servers) == 0 { + log.Error("No remote machines to gather stats from") + return + } + protips := new(protips) + + // Iterate over all the specified hosts and check their status + stats := tablewriter.NewWriter(os.Stdout) + stats.SetHeader([]string{"Server", "IP", "Status", "Service", "Details"}) + stats.SetColWidth(128) + + for _, server := range w.conf.Servers { + client := w.servers[server] + logger := log.New("server", server) + logger.Info("Starting remote server health-check") + + // If the server is not connected, try to connect again + if client == nil { + conn, err := dial(server) + if err != nil { + logger.Error("Failed to establish remote connection", "err", err) + stats.Append([]string{server, "", err.Error(), "", ""}) + continue + } + client = conn + } + // Client connected one way or another, run health-checks + services := make(map[string]string) + logger.Debug("Checking for nginx availability") + if infos, err := checkNginx(client, w.network); err != nil { + if err != ErrServiceUnknown { + services["nginx"] = err.Error() + } + } else { + services["nginx"] = infos.String() + } + logger.Debug("Checking for ethstats availability") + if infos, err := checkEthstats(client, w.network); err != nil { + if err != ErrServiceUnknown { + services["ethstats"] = err.Error() + } + } else { + services["ethstats"] = infos.String() + protips.ethstats = infos.config + } + logger.Debug("Checking for bootnode availability") + if infos, err := checkNode(client, w.network, true); err != nil { + if err != ErrServiceUnknown { + services["bootnode"] = err.Error() + } + } else { + services["bootnode"] = infos.String() + + protips.genesis = string(infos.genesis) + protips.bootFull = append(protips.bootFull, infos.enodeFull) + if infos.enodeLight != "" { + protips.bootLight = append(protips.bootLight, infos.enodeLight) + } + } + logger.Debug("Checking for sealnode availability") + if infos, err := checkNode(client, w.network, false); err != nil { + if err != ErrServiceUnknown { + services["sealnode"] = err.Error() + } + } else { + services["sealnode"] = infos.String() + protips.genesis = string(infos.genesis) + } + logger.Debug("Checking for faucet availability") + if infos, err := checkFaucet(client, w.network); err != nil { + if err != ErrServiceUnknown { + services["faucet"] = err.Error() + } + } else { + services["faucet"] = infos.String() + } + logger.Debug("Checking for dashboard availability") + if infos, err := checkDashboard(client, w.network); err != nil { + if err != ErrServiceUnknown { + services["dashboard"] = err.Error() + } + } else { + services["dashboard"] = infos.String() + } + // All status checks complete, report and check next server + delete(w.services, server) + for service := range services { + w.services[server] = append(w.services[server], service) + } + server, address := client.server, client.address + for service, status := range services { + stats.Append([]string{server, address, "online", service, status}) + server, address = "", "" + } + if len(services) == 0 { + stats.Append([]string{server, address, "online", "", ""}) + } + } + // If a genesis block was found, load it into our configs + if protips.genesis != "" { + genesis := new(core.Genesis) + if err := json.Unmarshal([]byte(protips.genesis), genesis); err != nil { + log.Error("Failed to parse remote genesis", "err", err) + } else { + w.conf.genesis = genesis + protips.network = genesis.Config.ChainId.Int64() + } + } + if protips.ethstats != "" { + w.conf.ethstats = protips.ethstats + } + w.conf.bootFull = protips.bootFull + w.conf.bootLight = protips.bootLight + + // Print any collected stats and return + if !tips { + stats.Render() + } else { + protips.print(w.network) + } +} + +// protips contains a collection of network infos to report pro-tips +// based on. +type protips struct { + genesis string + network int64 + bootFull []string + bootLight []string + ethstats string +} + +// print analyzes the network information available and prints a collection of +// pro tips for the user's consideration. +func (p *protips) print(network string) { + // If a known genesis block is available, display it and prepend an init command + fullinit, lightinit := "", "" + if p.genesis != "" { + fullinit = fmt.Sprintf("geth --datadir=$HOME/.%s init %s.json && ", network, network) + lightinit = fmt.Sprintf("geth --datadir=$HOME/.%s --light init %s.json && ", network, network) + } + // If an ethstats server is available, add the ethstats flag + statsflag := "" + if p.ethstats != "" { + if strings.Contains(p.ethstats, " ") { + statsflag = fmt.Sprintf(` --ethstats="yournode:%s"`, p.ethstats) + } else { + statsflag = fmt.Sprintf(` --ethstats=yournode:%s`, p.ethstats) + } + } + // If bootnodes have been specified, add the bootnode flag + bootflagFull := "" + if len(p.bootFull) > 0 { + bootflagFull = fmt.Sprintf(` --bootnodes %s`, strings.Join(p.bootFull, ",")) + } + bootflagLight := "" + if len(p.bootLight) > 0 { + bootflagLight = fmt.Sprintf(` --bootnodes %s`, strings.Join(p.bootLight, ",")) + } + // Assemble all the known pro-tips + var tasks, tips []string + + tasks = append(tasks, "Run an archive node with historical data") + tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=1024%s%s", fullinit, p.network, network, statsflag, bootflagFull)) + + tasks = append(tasks, "Run a full node with recent data only") + tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=512 --fast%s%s", fullinit, p.network, network, statsflag, bootflagFull)) + + tasks = append(tasks, "Run a light node with on demand retrievals") + tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --light%s%s", lightinit, p.network, network, statsflag, bootflagLight)) + + tasks = append(tasks, "Run an embedded node with constrained memory") + tips = append(tips, fmt.Sprintf("%sgeth --networkid=%d --datadir=$HOME/.%s --cache=32 --light%s%s", lightinit, p.network, network, statsflag, bootflagLight)) + + // If the tips are short, display in a table + short := true + for _, tip := range tips { + if len(tip) > 100 { + short = false + break + } + } + fmt.Println() + if short { + howto := tablewriter.NewWriter(os.Stdout) + howto.SetHeader([]string{"Fun tasks for you", "Tips on how to"}) + howto.SetColWidth(100) + + for i := 0; i < len(tasks); i++ { + howto.Append([]string{tasks[i], tips[i]}) + } + howto.Render() + return + } + // Meh, tips got ugly, split into many lines + for i := 0; i < len(tasks); i++ { + fmt.Println(tasks[i]) + fmt.Println(strings.Repeat("-", len(tasks[i]))) + fmt.Println(tips[i]) + fmt.Println() + fmt.Println() + } +} diff --git a/cmd/puppeth/wizard_network.go b/cmd/puppeth/wizard_network.go new file mode 100644 index 000000000..001d4e5b4 --- /dev/null +++ b/cmd/puppeth/wizard_network.go @@ -0,0 +1,194 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/log" +) + +// manageServers displays a list of servers the user can disconnect from, and an +// option to connect to new servers. +func (w *wizard) manageServers() { + // List all the servers we can disconnect, along with an entry to connect a new one + fmt.Println() + for i, server := range w.conf.Servers { + fmt.Printf(" %d. Disconnect %s\n", i+1, server) + } + fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1) + + choice := w.readInt() + if choice < 0 || choice > len(w.conf.Servers)+1 { + log.Error("Invalid server choice, aborting") + return + } + // If the user selected an existing server, drop it + if choice <= len(w.conf.Servers) { + server := w.conf.Servers[choice-1] + client := w.servers[server] + + delete(w.servers, server) + if client != nil { + client.Close() + } + w.conf.Servers = append(w.conf.Servers[:choice-1], w.conf.Servers[choice:]...) + w.conf.flush() + + log.Info("Disconnected existing server", "server", server) + w.networkStats(false) + return + } + // If the user requested connecting a new server, do it + if w.makeServer() != "" { + w.networkStats(false) + } +} + +// makeServer reads a single line from stdin and interprets it as a hostname to +// connect to. It tries to establish a new SSH session and also executing some +// baseline validations. +// +// If connection succeeds, the server is added to the wizards configs! +func (w *wizard) makeServer() string { + fmt.Println() + fmt.Println("Please enter remote server's address:") + + for { + // Read and fial the server to ensure docker is present + input := w.readString() + + client, err := dial(input) + if err != nil { + log.Error("Server not ready for puppeth", "err", err) + return "" + } + // All checks passed, start tracking the server + w.servers[input] = client + w.conf.Servers = append(w.conf.Servers, input) + w.conf.flush() + + return input + } +} + +// selectServer lists the user all the currnetly known servers to choose from, +// also granting the option to add a new one. +func (w *wizard) selectServer() string { + // List the available server to the user and wait for a choice + fmt.Println() + fmt.Println("Which server do you want to interact with?") + for i, server := range w.conf.Servers { + fmt.Printf(" %d. %s\n", i+1, server) + } + fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1) + + choice := w.readInt() + if choice < 0 || choice > len(w.conf.Servers)+1 { + log.Error("Invalid server choice, aborting") + return "" + } + // If the user requested connecting to a new server, go for it + if choice <= len(w.conf.Servers) { + return w.conf.Servers[choice-1] + } + return w.makeServer() +} + +// manageComponents displays a list of network components the user can tear down +// and an option +func (w *wizard) manageComponents() { + // List all the componens we can tear down, along with an entry to deploy a new one + fmt.Println() + + var serviceHosts, serviceNames []string + for server, services := range w.services { + for _, service := range services { + serviceHosts = append(serviceHosts, server) + serviceNames = append(serviceNames, service) + + fmt.Printf(" %d. Tear down %s on %s\n", len(serviceHosts), strings.Title(service), server) + } + } + fmt.Printf(" %d. Deploy new network component\n", len(serviceHosts)+1) + + choice := w.readInt() + if choice < 0 || choice > len(serviceHosts)+1 { + log.Error("Invalid component choice, aborting") + return + } + // If the user selected an existing service, destroy it + if choice <= len(serviceHosts) { + // Figure out the service to destroy and execute it + service := serviceNames[choice-1] + server := serviceHosts[choice-1] + client := w.servers[server] + + if out, err := tearDown(client, w.network, service, true); err != nil { + log.Error("Failed to tear down component", "err", err) + if len(out) > 0 { + fmt.Printf("%s\n", out) + } + return + } + // Clean up any references to it from out state + services := w.services[server] + for i, name := range services { + if name == service { + w.services[server] = append(services[:i], services[i+1:]...) + if len(w.services[server]) == 0 { + delete(w.services, server) + } + } + } + log.Info("Torn down existing component", "server", server, "service", service) + return + } + // If the user requested deploying a new component, do it + w.deployComponent() +} + +// deployComponent displays a list of network components the user can deploy and +// guides through the process. +func (w *wizard) deployComponent() { + // Print all the things we can deploy and wait or user choice + fmt.Println() + fmt.Println("What would you like to deploy? (recommended order)") + fmt.Println(" 1. Ethstats - Network monitoring tool") + fmt.Println(" 2. Bootnode - Entry point of the network") + fmt.Println(" 3. Sealer - Full node minting new blocks") + fmt.Println(" 4. Wallet - Browser wallet for quick sends (todo)") + fmt.Println(" 5. Faucet - Crypto faucet to give away funds") + fmt.Println(" 6. Dashboard - Website listing above web-services") + + switch w.read() { + case "1": + w.deployEthstats() + case "2": + w.deployNode(true) + case "3": + w.deployNode(false) + case "4": + case "5": + w.deployFaucet() + case "6": + w.deployDashboard() + default: + log.Error("That's not something I can do") + } +} diff --git a/cmd/puppeth/wizard_nginx.go b/cmd/puppeth/wizard_nginx.go new file mode 100644 index 000000000..86fba29f5 --- /dev/null +++ b/cmd/puppeth/wizard_nginx.go @@ -0,0 +1,58 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/log" +) + +// ensureVirtualHost checks whether a reverse-proxy is running on the specified +// host machine, and if yes requests a virtual host from the user to host a +// specific web service on. If no proxy exists, the method will offer to deploy +// one. +// +// If the user elects not to use a reverse proxy, an empty hostname is returned! +func (w *wizard) ensureVirtualHost(client *sshClient, port int, def string) (string, error) { + if proxy, _ := checkNginx(client, w.network); proxy != nil { + // Reverse proxy is running, if ports match, we need a virtual host + if proxy.port == port { + fmt.Println() + fmt.Printf("Shared port, which domain to assign? (default = %s)\n", def) + return w.readDefaultString(def), nil + } + } + // Reverse proxy is not running, offer to deploy a new one + fmt.Println() + fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)") + if w.readDefaultString("y") == "y" { + if out, err := deployNginx(client, w.network, port); err != nil { + log.Error("Failed to deploy reverse-proxy", "err", err) + if len(out) > 0 { + fmt.Printf("%s\n", out) + } + return "", err + } + // Reverse proxy deployed, ask again for the virtual-host + fmt.Println() + fmt.Printf("Proxy deployed, which domain to assign? (default = %s)\n", def) + return w.readDefaultString(def), nil + } + // Reverse proxy not requested, deploy as a standalone service + return "", nil +} diff --git a/cmd/puppeth/wizard_node.go b/cmd/puppeth/wizard_node.go new file mode 100644 index 000000000..d70d8f3c9 --- /dev/null +++ b/cmd/puppeth/wizard_node.go @@ -0,0 +1,153 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" +) + +// deployNode creates a new node configuration based on some user input. +func (w *wizard) deployNode(boot bool) { + // Do some sanity check before the user wastes time on input + if w.conf.genesis == nil { + log.Error("No genesis block configured") + return + } + if w.conf.ethstats == "" { + log.Error("No ethstats server configured") + return + } + // Select the server to interact with + server := w.selectServer() + if server == "" { + return + } + client := w.servers[server] + + // Retrieve any active ethstats configurations from the server + infos, err := checkNode(client, w.network, boot) + if err != nil { + if boot { + infos = &nodeInfos{portFull: 30303, peersTotal: 512, peersLight: 256} + } else { + infos = &nodeInfos{portFull: 30303, peersTotal: 50, peersLight: 0} + } + } + infos.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ") + infos.network = w.conf.genesis.Config.ChainId.Int64() + + // Figure out where the user wants to store the persistent data + fmt.Println() + if infos.datadir == "" { + fmt.Printf("Where should data be stored on the remote machine?\n") + infos.datadir = w.readString() + } else { + fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.datadir) + infos.datadir = w.readDefaultString(infos.datadir) + } + // Figure out which port to listen on + fmt.Println() + fmt.Printf("Which TCP/UDP port to listen on? (default = %d)\n", infos.portFull) + infos.portFull = w.readDefaultInt(infos.portFull) + + // Figure out how many peers to allow (different based on node type) + fmt.Println() + fmt.Printf("How many peers to allow connecting? (default = %d)\n", infos.peersTotal) + infos.peersTotal = w.readDefaultInt(infos.peersTotal) + + // Figure out how many light peers to allow (different based on node type) + fmt.Println() + fmt.Printf("How many light peers to allow connecting? (default = %d)\n", infos.peersLight) + infos.peersLight = w.readDefaultInt(infos.peersLight) + + // Set a proper name to report on the stats page + fmt.Println() + if infos.ethstats == "" { + fmt.Printf("What should the node be called on the stats page?\n") + infos.ethstats = w.readString() + ":" + w.conf.ethstats + } else { + fmt.Printf("What should the node be called on the stats page? (default = %s)\n", infos.ethstats) + infos.ethstats = w.readDefaultString(infos.ethstats) + ":" + w.conf.ethstats + } + // If the node is a miner/signer, load up needed credentials + if !boot { + if w.conf.genesis.Config.Ethash != nil { + // Ethash based miners only need an etherbase to mine against + fmt.Println() + if infos.etherbase == "" { + fmt.Printf("What address should the miner user?\n") + for { + if address := w.readAddress(); address != nil { + infos.etherbase = address.Hex() + break + } + } + } else { + fmt.Printf("What address should the miner user? (default = %s)\n", infos.etherbase) + infos.etherbase = w.readDefaultAddress(common.HexToAddress(infos.etherbase)).Hex() + } + } else if w.conf.genesis.Config.Clique != nil { + // If a previous signer was already set, offer to reuse it + if infos.keyJSON != "" { + var key keystore.Key + if err := json.Unmarshal([]byte(infos.keyJSON), &key); err != nil { + infos.keyJSON, infos.keyPass = "", "" + } else { + fmt.Println() + fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex()) + if w.readDefaultString("y") != "y" { + infos.keyJSON, infos.keyPass = "", "" + } + } + } + // Clique based signers need a keyfile and unlock password, ask if unavailable + if infos.keyJSON == "" { + fmt.Println() + fmt.Println("Please paste the signer's key JSON:") + infos.keyJSON = w.readJSON() + + fmt.Println() + fmt.Println("What's the unlock password for the account? (won't be echoed)") + infos.keyPass = w.readPassword() + + if _, err := keystore.DecryptKey([]byte(infos.keyJSON), infos.keyPass); err != nil { + log.Error("Failed to decrypt key with given passphrase") + return + } + } + } + } + // Try to deploy the full node on the host + if out, err := deployNode(client, w.network, w.conf.bootFull, infos); err != nil { + log.Error("Failed to deploy Ethereum node container", "err", err) + if len(out) > 0 { + fmt.Printf("%s\n", out) + } + return + } + // All ok, run a network scan to pick any changes up + log.Info("Waiting for node to finish booting") + time.Sleep(3 * time.Second) + + w.networkStats(false) +} diff --git a/cmd/rlpdump/main.go b/cmd/rlpdump/main.go index 10eea1fde..7d328e59b 100644 --- a/cmd/rlpdump/main.go +++ b/cmd/rlpdump/main.go @@ -32,6 +32,7 @@ import ( var ( hexMode = flag.String("hex", "", "dump given hex data") noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably") + single = flag.Bool("single", false, "print only the first element, discard the rest") ) func init() { @@ -82,6 +83,9 @@ func main() { break } fmt.Println() + if *single { + break + } } } diff --git a/cmd/swarm/list.go b/cmd/swarm/list.go new file mode 100644 index 000000000..06d3883cf --- /dev/null +++ b/cmd/swarm/list.go @@ -0,0 +1,61 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "fmt" + "os" + "strings" + "text/tabwriter" + + "github.com/ethereum/go-ethereum/cmd/utils" + swarm "github.com/ethereum/go-ethereum/swarm/api/client" + "gopkg.in/urfave/cli.v1" +) + +func list(ctx *cli.Context) { + args := ctx.Args() + + if len(args) < 1 { + utils.Fatalf("Please supply a manifest reference as the first argument") + } else if len(args) > 2 { + utils.Fatalf("Too many arguments - usage 'swarm ls manifest [prefix]'") + } + manifest := args[0] + + var prefix string + if len(args) == 2 { + prefix = args[1] + } + + bzzapi := strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client := swarm.NewClient(bzzapi) + list, err := client.List(manifest, prefix) + if err != nil { + utils.Fatalf("Failed to generate file and directory list: %s", err) + } + + w := tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0) + defer w.Flush() + fmt.Fprintln(w, "HASH\tCONTENT TYPE\tPATH") + for _, prefix := range list.CommonPrefixes { + fmt.Fprintf(w, "%s\t%s\t%s\n", "", "DIR", prefix) + } + for _, entry := range list.Entries { + fmt.Fprintf(w, "%s\t%s\t%s\n", entry.Hash, entry.ContentType, entry.Path) + } +} diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 171677146..26aa3e50f 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -39,19 +39,16 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/swarm" bzzapi "github.com/ethereum/go-ethereum/swarm/api" "gopkg.in/urfave/cli.v1" ) -const ( - clientIdentifier = "swarm" - versionString = "0.2" -) +const clientIdentifier = "swarm" var ( gitCommit string // Git SHA1 commit hash of the release (set via linker flags) - app = utils.NewApp(gitCommit, "Ethereum Swarm") testbetBootNodes = []string{ "enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429", "enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430", @@ -112,19 +109,36 @@ var ( Name: "defaultpath", Usage: "path to file served for empty url path (none)", } + SwarmUpFromStdinFlag = cli.BoolFlag{ + Name: "stdin", + Usage: "reads data to be uploaded from stdin", + } + SwarmUploadMimeType = cli.StringFlag{ + Name: "mime", + Usage: "force mime type", + } CorsStringFlag = cli.StringFlag{ Name: "corsdomain", Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')", } ) +var defaultNodeConfig = node.DefaultConfig + +// This init function sets defaults so cmd/swarm can run alongside geth. func init() { - // Override flag defaults so bzzd can run alongside geth. + defaultNodeConfig.Name = clientIdentifier + defaultNodeConfig.Version = params.VersionWithCommit(gitCommit) + defaultNodeConfig.P2P.ListenAddr = ":30399" + defaultNodeConfig.IPCPath = "bzzd.ipc" + // Set flag defaults for --help display. utils.ListenPortFlag.Value = 30399 - utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"} - utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3" +} + +var app = utils.NewApp(gitCommit, "Ethereum Swarm") - // Set up the cli app. +// This init function creates the cli.App. +func init() { app.Action = bzzd app.HideVersion = true // we have a command to print the version app.Copyright = "Copyright 2013-2016 The go-ethereum Authors" @@ -148,6 +162,15 @@ The output of this command is supposed to be machine-readable. `, }, { + Action: list, + Name: "ls", + Usage: "list files and directories contained in a manifest", + ArgsUsage: " <manifest> [<prefix>]", + Description: ` +Lists files and directories contained in a manifest. +`, + }, + { Action: hash, Name: "hash", Usage: "print the swarm hash of a file or directory", @@ -218,8 +241,8 @@ Cleans database of corrupted entries. utils.MaxPeersFlag, utils.NATFlag, utils.IPCDisabledFlag, - utils.IPCApiFlag, utils.IPCPathFlag, + utils.PasswordFileFlag, // bzzd-specific flags CorsStringFlag, EthAPIFlag, @@ -235,6 +258,8 @@ Cleans database of corrupted entries. SwarmRecursiveUploadFlag, SwarmWantManifestFlag, SwarmUploadDefaultPath, + SwarmUpFromStdinFlag, + SwarmUploadMimeType, } app.Flags = append(app.Flags, debug.Flags...) app.Before = func(ctx *cli.Context) error { @@ -256,7 +281,7 @@ func main() { func version(ctx *cli.Context) error { fmt.Println(strings.Title(clientIdentifier)) - fmt.Println("Version:", versionString) + fmt.Println("Version:", params.Version) if gitCommit != "" { fmt.Println("Git Commit:", gitCommit) } @@ -269,9 +294,16 @@ func version(ctx *cli.Context) error { } func bzzd(ctx *cli.Context) error { - stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) + cfg := defaultNodeConfig + utils.SetNodeConfig(ctx, &cfg) + stack, err := node.New(&cfg) + if err != nil { + utils.Fatalf("can't create node: %v", err) + } + registerBzzService(ctx, stack) utils.StartNode(stack) + go func() { sigc := make(chan os.Signal, 1) signal.Notify(sigc, syscall.SIGTERM) @@ -280,6 +312,7 @@ func bzzd(ctx *cli.Context) error { log.Info("Got sigterm, shutting swarm down...") stack.Stop() }() + networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name) // Add bootnodes as initial peers. if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { @@ -296,7 +329,6 @@ func bzzd(ctx *cli.Context) error { } func registerBzzService(ctx *cli.Context, stack *node.Node) { - prvkey := getAccount(ctx, stack) chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name)) @@ -326,6 +358,8 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) { if err != nil { utils.Fatalf("Can't connect: %v", err) } + } else { + swapEnabled = false } return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors) } @@ -349,10 +383,10 @@ func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { am := stack.AccountManager() ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) - return decryptStoreAccount(ks, keyid) + return decryptStoreAccount(ks, keyid, utils.MakePasswordList(ctx)) } -func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKey { +func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey { var a accounts.Account var err error if common.IsHexAddress(account) { @@ -373,9 +407,9 @@ func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKe if err != nil { utils.Fatalf("Can't load swarm account key: %v", err) } - for i := 1; i <= 3; i++ { - passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i)) - key, err := keystore.DecryptKey(keyjson, passphrase) + for i := 0; i < 3; i++ { + password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords) + key, err := keystore.DecryptKey(keyjson, password) if err == nil { return key.PrivateKey } @@ -384,7 +418,18 @@ func decryptStoreAccount(ks *keystore.KeyStore, account string) *ecdsa.PrivateKe return nil } -func promptPassphrase(prompt string) string { +// getPassPhrase retrieves the password associated with bzz account, either by fetching +// from a list of pre-loaded passwords, or by requesting it interactively from user. +func getPassPhrase(prompt string, i int, passwords []string) string { + // non-interactive + if len(passwords) > 0 { + if i < len(passwords) { + return passwords[i] + } + return passwords[len(passwords)-1] + } + + // fallback to interactive mode if prompt != "" { fmt.Println(prompt) } diff --git a/cmd/swarm/manifest.go b/cmd/swarm/manifest.go index 2b6b02313..9729022c0 100644 --- a/cmd/swarm/manifest.go +++ b/cmd/swarm/manifest.go @@ -25,6 +25,8 @@ import ( "strings" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/swarm/api" + swarm "github.com/ethereum/go-ethereum/swarm/api/client" "gopkg.in/urfave/cli.v1" ) @@ -41,7 +43,7 @@ func add(ctx *cli.Context) { ctype string wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) - mroot manifest + mroot api.Manifest ) if len(args) > 3 { @@ -75,7 +77,7 @@ func update(ctx *cli.Context) { ctype string wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) - mroot manifest + mroot api.Manifest ) if len(args) > 3 { ctype = args[3] @@ -105,7 +107,7 @@ func remove(ctx *cli.Context) { path = args[1] wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) - mroot manifest + mroot api.Manifest ) newManifest := removeEntryFromManifest(ctx, mhash, path) @@ -123,21 +125,17 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin var ( bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") - client = &client{api: bzzapi} - longestPathEntry = manifestEntry{ - Path: "", - Hash: "", - ContentType: "", - } + client = swarm.NewClient(bzzapi) + longestPathEntry = api.ManifestEntry{} ) - mroot, err := client.downloadManifest(mhash) + mroot, err := client.DownloadManifest(mhash) if err != nil { utils.Fatalf("Manifest download failed: %v", err) } //TODO: check if the "hash" to add is valid and present in swarm - _, err = client.downloadManifest(hash) + _, err = client.DownloadManifest(hash) if err != nil { utils.Fatalf("Hash to add is not present: %v", err) } @@ -162,7 +160,7 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin newHash := addEntryToManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype) // Replace the hash for parent Manifests - newMRoot := manifest{} + newMRoot := &api.Manifest{} for _, entry := range mroot.Entries { if longestPathEntry.Path == entry.Path { entry.Hash = newHash @@ -172,15 +170,15 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin mroot = newMRoot } else { // Add the entry in the leaf Manifest - newEntry := manifestEntry{ - Path: path, + newEntry := api.ManifestEntry{ Hash: hash, + Path: path, ContentType: ctype, } mroot.Entries = append(mroot.Entries, newEntry) } - newManifestHash, err := client.uploadManifest(mroot) + newManifestHash, err := client.UploadManifest(mroot) if err != nil { utils.Fatalf("Manifest upload failed: %v", err) } @@ -191,21 +189,13 @@ func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) strin func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) string { var ( - bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") - client = &client{api: bzzapi} - newEntry = manifestEntry{ - Path: "", - Hash: "", - ContentType: "", - } - longestPathEntry = manifestEntry{ - Path: "", - Hash: "", - ContentType: "", - } + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client = swarm.NewClient(bzzapi) + newEntry = api.ManifestEntry{} + longestPathEntry = api.ManifestEntry{} ) - mroot, err := client.downloadManifest(mhash) + mroot, err := client.DownloadManifest(mhash) if err != nil { utils.Fatalf("Manifest download failed: %v", err) } @@ -236,7 +226,7 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st newHash := updateEntryInManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype) // Replace the hash for parent Manifests - newMRoot := manifest{} + newMRoot := &api.Manifest{} for _, entry := range mroot.Entries { if longestPathEntry.Path == entry.Path { entry.Hash = newHash @@ -249,12 +239,12 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st if newEntry.Path != "" { // Replace the hash for leaf Manifest - newMRoot := manifest{} + newMRoot := &api.Manifest{} for _, entry := range mroot.Entries { if newEntry.Path == entry.Path { - myEntry := manifestEntry{ - Path: entry.Path, + myEntry := api.ManifestEntry{ Hash: hash, + Path: entry.Path, ContentType: ctype, } newMRoot.Entries = append(newMRoot.Entries, myEntry) @@ -265,7 +255,7 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st mroot = newMRoot } - newManifestHash, err := client.uploadManifest(mroot) + newManifestHash, err := client.UploadManifest(mroot) if err != nil { utils.Fatalf("Manifest upload failed: %v", err) } @@ -275,21 +265,13 @@ func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) st func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string { var ( - bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") - client = &client{api: bzzapi} - entryToRemove = manifestEntry{ - Path: "", - Hash: "", - ContentType: "", - } - longestPathEntry = manifestEntry{ - Path: "", - Hash: "", - ContentType: "", - } + bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") + client = swarm.NewClient(bzzapi) + entryToRemove = api.ManifestEntry{} + longestPathEntry = api.ManifestEntry{} ) - mroot, err := client.downloadManifest(mhash) + mroot, err := client.DownloadManifest(mhash) if err != nil { utils.Fatalf("Manifest download failed: %v", err) } @@ -318,7 +300,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string { newHash := removeEntryFromManifest(ctx, longestPathEntry.Hash, newPath) // Replace the hash for parent Manifests - newMRoot := manifest{} + newMRoot := &api.Manifest{} for _, entry := range mroot.Entries { if longestPathEntry.Path == entry.Path { entry.Hash = newHash @@ -330,7 +312,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string { if entryToRemove.Path != "" { // remove the entry in this Manifest - newMRoot := manifest{} + newMRoot := &api.Manifest{} for _, entry := range mroot.Entries { if entryToRemove.Path != entry.Path { newMRoot.Entries = append(newMRoot.Entries, entry) @@ -339,7 +321,7 @@ func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string { mroot = newMRoot } - newManifestHash, err := client.uploadManifest(mroot) + newManifestHash, err := client.UploadManifest(mroot) if err != nil { utils.Fatalf("Manifest upload failed: %v", err) } diff --git a/cmd/swarm/upload.go b/cmd/swarm/upload.go index 7b4961778..42673ae21 100644 --- a/cmd/swarm/upload.go +++ b/cmd/swarm/upload.go @@ -18,8 +18,6 @@ package main import ( - "bytes" - "encoding/json" "fmt" "io" "io/ioutil" @@ -32,58 +30,83 @@ import ( "strings" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/log" + swarm "github.com/ethereum/go-ethereum/swarm/api/client" "gopkg.in/urfave/cli.v1" ) func upload(ctx *cli.Context) { + args := ctx.Args() var ( bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/") recursive = ctx.GlobalBool(SwarmRecursiveUploadFlag.Name) wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name) defaultPath = ctx.GlobalString(SwarmUploadDefaultPath.Name) + fromStdin = ctx.GlobalBool(SwarmUpFromStdinFlag.Name) + mimeType = ctx.GlobalString(SwarmUploadMimeType.Name) + client = swarm.NewClient(bzzapi) + file string ) + if len(args) != 1 { - utils.Fatalf("Need filename as the first and only argument") + if fromStdin { + tmp, err := ioutil.TempFile("", "swarm-stdin") + if err != nil { + utils.Fatalf("error create tempfile: %s", err) + } + defer os.Remove(tmp.Name()) + n, err := io.Copy(tmp, os.Stdin) + if err != nil { + utils.Fatalf("error copying stdin to tempfile: %s", err) + } else if n == 0 { + utils.Fatalf("error reading from stdin: zero length") + } + file = tmp.Name() + } else { + utils.Fatalf("Need filename as the first and only argument") + } + } else { + file = expandPath(args[0]) } - var ( - file = args[0] - client = &client{api: bzzapi} - ) - fi, err := os.Stat(expandPath(file)) + if !wantManifest { + f, err := swarm.Open(file) + if err != nil { + utils.Fatalf("Error opening file: %s", err) + } + defer f.Close() + hash, err := client.UploadRaw(f, f.Size) + if err != nil { + utils.Fatalf("Upload failed: %s", err) + } + fmt.Println(hash) + return + } + + stat, err := os.Stat(file) if err != nil { - utils.Fatalf("Failed to stat file: %v", err) + utils.Fatalf("Error opening file: %s", err) } - if fi.IsDir() { + var hash string + if stat.IsDir() { if !recursive { utils.Fatalf("Argument is a directory and recursive upload is disabled") } - if !wantManifest { - utils.Fatalf("Manifest is required for directory uploads") + hash, err = client.UploadDirectory(file, defaultPath, "") + } else { + if mimeType == "" { + mimeType = detectMimeType(file) } - mhash, err := client.uploadDirectory(file, defaultPath) + f, err := swarm.Open(file) if err != nil { - utils.Fatalf("Failed to upload directory: %v", err) + utils.Fatalf("Error opening file: %s", err) } - fmt.Println(mhash) - return - } - entry, err := client.uploadFile(file, fi) - if err != nil { - utils.Fatalf("Upload failed: %v", err) - } - mroot := manifest{[]manifestEntry{entry}} - if !wantManifest { - // Print the manifest. This is the only output to stdout. - mrootJSON, _ := json.MarshalIndent(mroot, "", " ") - fmt.Println(string(mrootJSON)) - return + defer f.Close() + f.ContentType = mimeType + hash, err = client.Upload(f, "") } - hash, err := client.uploadManifest(mroot) if err != nil { - utils.Fatalf("Manifest upload failed: %v", err) + utils.Fatalf("Upload failed: %s", err) } fmt.Println(hash) } @@ -112,147 +135,18 @@ func homeDir() string { return "" } -// client wraps interaction with the swarm HTTP gateway. -type client struct { - api string -} - -// manifest is the JSON representation of a swarm manifest. -type manifestEntry struct { - Hash string `json:"hash,omitempty"` - ContentType string `json:"contentType,omitempty"` - Path string `json:"path,omitempty"` -} - -// manifest is the JSON representation of a swarm manifest. -type manifest struct { - Entries []manifestEntry `json:"entries,omitempty"` -} - -func (c *client) uploadDirectory(dir string, defaultPath string) (string, error) { - mhash, err := c.postRaw("application/json", 2, ioutil.NopCloser(bytes.NewReader([]byte("{}")))) - if err != nil { - return "", fmt.Errorf("failed to upload empty manifest") - } - if len(defaultPath) > 0 { - fi, err := os.Stat(defaultPath) - if err != nil { - return "", err - } - mhash, err = c.uploadToManifest(mhash, "", defaultPath, fi) - if err != nil { - return "", err - } - } - prefix := filepath.ToSlash(filepath.Clean(dir)) + "/" - err = filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { - if err != nil || fi.IsDir() { - return err - } - if !strings.HasPrefix(path, dir) { - return fmt.Errorf("path %s outside directory %s", path, dir) - } - uripath := strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix) - mhash, err = c.uploadToManifest(mhash, uripath, path, fi) - return err - }) - return mhash, err -} - -func (c *client) uploadFile(file string, fi os.FileInfo) (manifestEntry, error) { - hash, err := c.uploadFileContent(file, fi) - m := manifestEntry{ - Hash: hash, - ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())), +func detectMimeType(file string) string { + if ext := filepath.Ext(file); ext != "" { + return mime.TypeByExtension(ext) } - return m, err -} - -func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error) { - fd, err := os.Open(file) - if err != nil { - return "", err - } - defer fd.Close() - log.Info("Uploading swarm content", "file", file, "bytes", fi.Size()) - return c.postRaw("application/octet-stream", fi.Size(), fd) -} - -func (c *client) uploadManifest(m manifest) (string, error) { - jsm, err := json.Marshal(m) - if err != nil { - panic(err) - } - log.Info("Uploading swarm manifest") - return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm))) -} - -func (c *client) uploadToManifest(mhash string, path string, fpath string, fi os.FileInfo) (string, error) { - fd, err := os.Open(fpath) - if err != nil { - return "", err - } - defer fd.Close() - log.Info("Uploading swarm content and path", "file", fpath, "bytes", fi.Size(), "path", path) - req, err := http.NewRequest("PUT", c.api+"/bzz:/"+mhash+"/"+path, fd) - if err != nil { - return "", err - } - req.Header.Set("content-type", mime.TypeByExtension(filepath.Ext(fi.Name()))) - req.ContentLength = fi.Size() - resp, err := http.DefaultClient.Do(req) + f, err := os.Open(file) if err != nil { - return "", err + return "" } - defer resp.Body.Close() - if resp.StatusCode >= 400 { - return "", fmt.Errorf("bad status: %s", resp.Status) - } - content, err := ioutil.ReadAll(resp.Body) - return string(content), err -} - -func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (string, error) { - req, err := http.NewRequest("POST", c.api+"/bzzr:/", body) - if err != nil { - return "", err + defer f.Close() + buf := make([]byte, 512) + if n, _ := f.Read(buf); n > 0 { + return http.DetectContentType(buf) } - req.Header.Set("content-type", mimetype) - req.ContentLength = size - resp, err := http.DefaultClient.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - if resp.StatusCode >= 400 { - return "", fmt.Errorf("bad status: %s", resp.Status) - } - content, err := ioutil.ReadAll(resp.Body) - return string(content), err -} - -func (c *client) downloadManifest(mhash string) (manifest, error) { - - mroot := manifest{} - req, err := http.NewRequest("GET", c.api+"/bzzr:/"+mhash, nil) - if err != nil { - return mroot, err - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return mroot, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return mroot, fmt.Errorf("bad status: %s", resp.Status) - - } - content, err := ioutil.ReadAll(resp.Body) - - err = json.Unmarshal(content, &mroot) - if err != nil { - return mroot, fmt.Errorf("Manifest %v is malformed: %v", mhash, err) - } - return mroot, err + return "" } diff --git a/cmd/utils/customflags.go b/cmd/utils/customflags.go index 00f28f2ec..e5bf8724c 100644 --- a/cmd/utils/customflags.go +++ b/cmd/utils/customflags.go @@ -17,6 +17,7 @@ package utils import ( + "encoding" "errors" "flag" "fmt" @@ -78,6 +79,58 @@ func (self DirectoryFlag) Apply(set *flag.FlagSet) { }) } +type TextMarshaler interface { + encoding.TextMarshaler + encoding.TextUnmarshaler +} + +// textMarshalerVal turns a TextMarshaler into a flag.Value +type textMarshalerVal struct { + v TextMarshaler +} + +func (v textMarshalerVal) String() string { + if v.v == nil { + return "" + } + text, _ := v.v.MarshalText() + return string(text) +} + +func (v textMarshalerVal) Set(s string) error { + return v.v.UnmarshalText([]byte(s)) +} + +// TextMarshalerFlag wraps a TextMarshaler value. +type TextMarshalerFlag struct { + Name string + Value TextMarshaler + Usage string +} + +func (f TextMarshalerFlag) GetName() string { + return f.Name +} + +func (f TextMarshalerFlag) String() string { + return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage) +} + +func (f TextMarshalerFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage) + }) +} + +// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set. +func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { + val := ctx.GlobalGeneric(name) + if val == nil { + return nil + } + return val.(textMarshalerVal).v +} + // BigFlag is a command line flag that accepts 256 bit big integers in decimal or // hexadecimal syntax. type BigFlag struct { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 38c90d801..b35574c86 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -23,7 +23,6 @@ import ( "io/ioutil" "math/big" "os" - "os/user" "path/filepath" "runtime" "strconv" @@ -32,11 +31,14 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "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/state" "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/downloader" + "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/event" @@ -44,13 +46,12 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" - "github.com/ethereum/go-ethereum/rpc" whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" "gopkg.in/urfave/cli.v1" ) @@ -121,35 +122,36 @@ var ( EthashCachesInMemoryFlag = cli.IntFlag{ Name: "ethash.cachesinmem", Usage: "Number of recent ethash caches to keep in memory (16MB each)", - Value: 2, + Value: eth.DefaultConfig.EthashCachesInMem, } EthashCachesOnDiskFlag = cli.IntFlag{ Name: "ethash.cachesondisk", Usage: "Number of recent ethash caches to keep on disk (16MB each)", - Value: 3, + Value: eth.DefaultConfig.EthashCachesOnDisk, } EthashDatasetDirFlag = DirectoryFlag{ Name: "ethash.dagdir", Usage: "Directory to store the ethash mining DAGs (default = inside home folder)", + Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir}, } EthashDatasetsInMemoryFlag = cli.IntFlag{ Name: "ethash.dagsinmem", Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)", - Value: 1, + Value: eth.DefaultConfig.EthashDatasetsInMem, } EthashDatasetsOnDiskFlag = cli.IntFlag{ Name: "ethash.dagsondisk", Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)", - Value: 2, + Value: eth.DefaultConfig.EthashDatasetsOnDisk, } NetworkIdFlag = cli.IntFlag{ Name: "networkid", Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)", - Value: eth.NetworkId, + Value: eth.DefaultConfig.NetworkId, } TestNetFlag = cli.BoolFlag{ Name: "testnet", - Usage: "Ropsten network: pre-configured test network", + Usage: "Ropsten network: pre-configured proof-of-work test network", } DevModeFlag = cli.BoolFlag{ Name: "dev", @@ -172,6 +174,13 @@ var ( Name: "light", Usage: "Enable light client mode", } + defaultSyncMode = eth.DefaultConfig.SyncMode + SyncModeFlag = TextMarshalerFlag{ + Name: "syncmode", + Usage: `Blockchain sync mode ("fast", "full", or "light")`, + Value: &defaultSyncMode, + } + LightServFlag = cli.IntFlag{ Name: "lightserv", Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", @@ -238,19 +247,6 @@ var ( Value: "", } - VMForceJitFlag = cli.BoolFlag{ - Name: "forcejit", - Usage: "Force the JIT VM to take precedence", - } - VMJitCacheFlag = cli.IntFlag{ - Name: "jitcache", - Usage: "Amount of cached JIT VM programs", - Value: 64, - } - VMEnableJitFlag = cli.BoolFlag{ - Name: "jitvm", - Usage: "Enable the JIT VM", - } VMEnableDebugFlag = cli.BoolFlag{ Name: "vmdebug", Usage: "Record information useful for VM and contract debugging", @@ -295,21 +291,15 @@ var ( RPCApiFlag = cli.StringFlag{ Name: "rpcapi", Usage: "API's offered over the HTTP-RPC interface", - Value: rpc.DefaultHTTPApis, + Value: "", } IPCDisabledFlag = cli.BoolFlag{ Name: "ipcdisable", Usage: "Disable the IPC-RPC server", } - IPCApiFlag = cli.StringFlag{ - Name: "ipcapi", - Usage: "APIs offered over the IPC-RPC interface", - Value: rpc.DefaultIPCApis, - } IPCPathFlag = DirectoryFlag{ Name: "ipcpath", Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", - Value: DirectoryString{"geth.ipc"}, } WSEnabledFlag = cli.BoolFlag{ Name: "ws", @@ -328,7 +318,7 @@ var ( WSApiFlag = cli.StringFlag{ Name: "wsapi", Usage: "API's offered over the WS-RPC interface", - Value: rpc.DefaultHTTPApis, + Value: "", } WSAllowedOriginsFlag = cli.StringFlag{ Name: "wsorigins", @@ -402,42 +392,17 @@ var ( Usage: "JavaScript root path for `loadScript`", Value: ".", } - SolcPathFlag = cli.StringFlag{ - Name: "solc", - Usage: "Solidity compiler command to be used", - Value: "solc", - } // Gas price oracle settings - GpoMinGasPriceFlag = BigFlag{ - Name: "gpomin", - Usage: "Minimum suggested gas price", - Value: big.NewInt(20 * params.Shannon), + GpoBlocksFlag = cli.IntFlag{ + Name: "gpoblocks", + Usage: "Number of recent blocks to check for gas prices", + Value: eth.DefaultConfig.GPO.Blocks, } - GpoMaxGasPriceFlag = BigFlag{ - Name: "gpomax", - Usage: "Maximum suggested gas price", - Value: big.NewInt(500 * params.Shannon), - } - GpoFullBlockRatioFlag = cli.IntFlag{ - Name: "gpofull", - Usage: "Full block threshold for gas price calculation (%)", - Value: 80, - } - GpobaseStepDownFlag = cli.IntFlag{ - Name: "gpobasedown", - Usage: "Suggested gas price base step down ratio (1/1000)", - Value: 10, - } - GpobaseStepUpFlag = cli.IntFlag{ - Name: "gpobaseup", - Usage: "Suggested gas price base step up ratio (1/1000)", - Value: 100, - } - GpobaseCorrectionFactorFlag = cli.IntFlag{ - Name: "gpobasecf", - Usage: "Suggested gas price base correction factor (%)", - Value: 110, + GpoPercentileFlag = cli.IntFlag{ + Name: "gpopercentile", + Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", + Value: eth.DefaultConfig.GPO.Percentile, } ) @@ -456,88 +421,42 @@ func MakeDataDir(ctx *cli.Context) string { return "" } -// MakeEthashCacheDir returns the directory to use for storing the ethash cache -// dumps. -func MakeEthashCacheDir(ctx *cli.Context) string { - if ctx.GlobalIsSet(EthashCacheDirFlag.Name) && ctx.GlobalString(EthashCacheDirFlag.Name) == "" { - return "" - } - if !ctx.GlobalIsSet(EthashCacheDirFlag.Name) { - return "ethash" - } - return ctx.GlobalString(EthashCacheDirFlag.Name) -} - -// MakeEthashDatasetDir returns the directory to use for storing the full ethash -// dataset dumps. -func MakeEthashDatasetDir(ctx *cli.Context) string { - if !ctx.GlobalIsSet(EthashDatasetDirFlag.Name) { - home := os.Getenv("HOME") - if home == "" { - if user, err := user.Current(); err == nil { - home = user.HomeDir - } - } - if runtime.GOOS == "windows" { - return filepath.Join(home, "AppData", "Ethash") - } - return filepath.Join(home, ".ethash") - } - return ctx.GlobalString(EthashDatasetDirFlag.Name) -} - -// MakeIPCPath creates an IPC path configuration from the set command line flags, -// returning an empty string if IPC was explicitly disabled, or the set path. -func MakeIPCPath(ctx *cli.Context) string { - if ctx.GlobalBool(IPCDisabledFlag.Name) { - return "" - } - return ctx.GlobalString(IPCPathFlag.Name) -} - -// MakeNodeKey creates a node key from set command line flags, either loading it +// setNodeKey creates a node key from set command line flags, either loading it // from a file or as a specified hex value. If neither flags were provided, this // method returns nil and an emphemeral key is to be generated. -func MakeNodeKey(ctx *cli.Context) *ecdsa.PrivateKey { +func setNodeKey(ctx *cli.Context, cfg *p2p.Config) { var ( hex = ctx.GlobalString(NodeKeyHexFlag.Name) file = ctx.GlobalString(NodeKeyFileFlag.Name) - - key *ecdsa.PrivateKey - err error + key *ecdsa.PrivateKey + err error ) switch { case file != "" && hex != "": Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name) - case file != "": if key, err = crypto.LoadECDSA(file); err != nil { Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err) } - + cfg.PrivateKey = key case hex != "": if key, err = crypto.HexToECDSA(hex); err != nil { Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err) } + cfg.PrivateKey = key } - return key } -// makeNodeUserIdent creates the user identifier from CLI flags. -func makeNodeUserIdent(ctx *cli.Context) string { - var comps []string +// setNodeUserIdent creates the user identifier from CLI flags. +func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) { if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 { - comps = append(comps, identity) - } - if ctx.GlobalBool(VMEnableJitFlag.Name) { - comps = append(comps, "JIT") + cfg.UserIdent = identity } - return strings.Join(comps, "/") } -// MakeBootstrapNodes creates a list of bootstrap nodes from the command line +// setBootstrapNodes creates a list of bootstrap nodes from the command line // flags, reverting to pre-configured ones if none have been specified. -func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node { +func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { urls := params.MainnetBootnodes if ctx.GlobalIsSet(BootnodesFlag.Name) { urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") @@ -545,62 +464,68 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node { urls = params.TestnetBootnodes } - bootnodes := make([]*discover.Node, 0, len(urls)) + cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls)) for _, url := range urls { node, err := discover.ParseNode(url) if err != nil { log.Error("Bootstrap URL invalid", "enode", url, "err", err) continue } - bootnodes = append(bootnodes, node) + cfg.BootstrapNodes = append(cfg.BootstrapNodes, node) } - return bootnodes } -// MakeBootstrapNodesV5 creates a list of bootstrap nodes from the command line +// setBootstrapNodesV5 creates a list of bootstrap nodes from the command line // flags, reverting to pre-configured ones if none have been specified. -func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node { +func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) { urls := params.DiscoveryV5Bootnodes if ctx.GlobalIsSet(BootnodesFlag.Name) { urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") + } else if cfg.BootstrapNodesV5 == nil { + return // already set, don't apply defaults. } - bootnodes := make([]*discv5.Node, 0, len(urls)) + cfg.BootstrapNodesV5 = make([]*discv5.Node, 0, len(urls)) for _, url := range urls { node, err := discv5.ParseNode(url) if err != nil { log.Error("Bootstrap URL invalid", "enode", url, "err", err) continue } - bootnodes = append(bootnodes, node) + cfg.BootstrapNodesV5 = append(cfg.BootstrapNodesV5, node) } - return bootnodes } -// MakeListenAddress creates a TCP listening address string from set command +// setListenAddress creates a TCP listening address string from set command // line flags. -func MakeListenAddress(ctx *cli.Context) string { - return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)) +func setListenAddress(ctx *cli.Context, cfg *p2p.Config) { + if ctx.GlobalIsSet(ListenPortFlag.Name) { + cfg.ListenAddr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)) + } } -// MakeDiscoveryV5Address creates a UDP listening address string from set command +// setDiscoveryV5Address creates a UDP listening address string from set command // line flags for the V5 discovery protocol. -func MakeDiscoveryV5Address(ctx *cli.Context) string { - return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1) +func setDiscoveryV5Address(ctx *cli.Context, cfg *p2p.Config) { + if ctx.GlobalIsSet(ListenPortFlag.Name) { + cfg.DiscoveryV5Addr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1) + } } -// MakeNAT creates a port mapper from set command line flags. -func MakeNAT(ctx *cli.Context) nat.Interface { - natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name)) - if err != nil { - Fatalf("Option %s: %v", NATFlag.Name, err) +// setNAT creates a port mapper from command line flags. +func setNAT(ctx *cli.Context, cfg *p2p.Config) { + if ctx.GlobalIsSet(NATFlag.Name) { + natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name)) + if err != nil { + Fatalf("Option %s: %v", NATFlag.Name, err) + } + cfg.NAT = natif } - return natif } -// MakeRPCModules splits input separated by a comma and trims excessive white -// space from the substrings. -func MakeRPCModules(input string) []string { +// splitAndTrim splits input separated by a comma +// and trims excessive white space from the substrings. +func splitAndTrim(input string) []string { result := strings.Split(input, ",") for i, r := range result { result[i] = strings.TrimSpace(r) @@ -608,27 +533,63 @@ func MakeRPCModules(input string) []string { return result } -// MakeHTTPRpcHost creates the HTTP RPC listener interface string from the set +// setHTTP creates the HTTP RPC listener interface string from the set // command line flags, returning empty if the HTTP endpoint is disabled. -func MakeHTTPRpcHost(ctx *cli.Context) string { - if !ctx.GlobalBool(RPCEnabledFlag.Name) { - return "" +func setHTTP(ctx *cli.Context, cfg *node.Config) { + if ctx.GlobalBool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" { + cfg.HTTPHost = "127.0.0.1" + if ctx.GlobalIsSet(RPCListenAddrFlag.Name) { + cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name) + } + } + + if ctx.GlobalIsSet(RPCPortFlag.Name) { + cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name) + } + if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) { + cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name)) + } + if ctx.GlobalIsSet(RPCApiFlag.Name) { + cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name)) } - return ctx.GlobalString(RPCListenAddrFlag.Name) } -// MakeWSRpcHost creates the WebSocket RPC listener interface string from the set +// setWS creates the WebSocket RPC listener interface string from the set // command line flags, returning empty if the HTTP endpoint is disabled. -func MakeWSRpcHost(ctx *cli.Context) string { - if !ctx.GlobalBool(WSEnabledFlag.Name) { - return "" +func setWS(ctx *cli.Context, cfg *node.Config) { + if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" { + cfg.WSHost = "127.0.0.1" + if ctx.GlobalIsSet(WSListenAddrFlag.Name) { + cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name) + } + } + + if ctx.GlobalIsSet(WSPortFlag.Name) { + cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name) + } + if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) { + cfg.WSOrigins = splitAndTrim(ctx.GlobalString(WSAllowedOriginsFlag.Name)) + } + if ctx.GlobalIsSet(WSApiFlag.Name) { + cfg.WSModules = splitAndTrim(ctx.GlobalString(WSApiFlag.Name)) } - return ctx.GlobalString(WSListenAddrFlag.Name) } -// MakeDatabaseHandles raises out the number of allowed file handles per process +// setIPC creates an IPC path configuration from the set command line flags, +// returning an empty string if IPC was explicitly disabled, or the set path. +func setIPC(ctx *cli.Context, cfg *node.Config) { + checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag) + switch { + case ctx.GlobalBool(IPCDisabledFlag.Name): + cfg.IPCPath = "" + case ctx.GlobalIsSet(IPCPathFlag.Name): + cfg.IPCPath = ctx.GlobalString(IPCPathFlag.Name) + } +} + +// makeDatabaseHandles raises out the number of allowed file handles per process // for Geth and returns half of the allowance to assign to the database. -func MakeDatabaseHandles() int { +func makeDatabaseHandles() int { if err := raiseFdLimit(2048); err != nil { Fatalf("Failed to raise file descriptor allowance: %v", err) } @@ -661,33 +622,25 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error return accs[index], nil } -// MakeEtherbase retrieves the etherbase either from the directly specified +// setEtherbase retrieves the etherbase either from the directly specified // command line flags or from the keystore if CLI indexed. -func MakeEtherbase(ks *keystore.KeyStore, ctx *cli.Context) common.Address { - accounts := ks.Accounts() - if !ctx.GlobalIsSet(EtherbaseFlag.Name) && len(accounts) == 0 { - log.Warn("No etherbase set and no accounts found as default") - return common.Address{} - } - etherbase := ctx.GlobalString(EtherbaseFlag.Name) - if etherbase == "" { - return common.Address{} - } - // If the specified etherbase is a valid address, return it - account, err := MakeAddress(ks, etherbase) - if err != nil { - Fatalf("Option %q: %v", EtherbaseFlag.Name, err) +func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) { + if ctx.GlobalIsSet(EtherbaseFlag.Name) { + account, err := MakeAddress(ks, ctx.GlobalString(EtherbaseFlag.Name)) + if err != nil { + Fatalf("Option %q: %v", EtherbaseFlag.Name, err) + } + cfg.Etherbase = account.Address + return } - return account.Address -} - -// MakeMinerExtra resolves extradata for the miner from the set command line flags -// or returns a default one composed on the client, runtime and OS metadata. -func MakeMinerExtra(extra []byte, ctx *cli.Context) []byte { - if ctx.GlobalIsSet(ExtraDataFlag.Name) { - return []byte(ctx.GlobalString(ExtraDataFlag.Name)) + accounts := ks.Accounts() + if (cfg.Etherbase == common.Address{}) { + if len(accounts) > 0 { + cfg.Etherbase = accounts[0].Address + } else { + log.Warn("No etherbase set and no accounts found as default") + } } - return extra } // MakePasswordList reads password lines from the file specified by --password. @@ -708,150 +661,213 @@ func MakePasswordList(ctx *cli.Context) []string { return lines } -// MakeNode configures a node with no services from command line flags. -func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node { - vsn := params.Version - if gitCommit != "" { - vsn += "-" + gitCommit[:8] +func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { + setNodeKey(ctx, cfg) + setNAT(ctx, cfg) + setListenAddress(ctx, cfg) + setDiscoveryV5Address(ctx, cfg) + setBootstrapNodes(ctx, cfg) + setBootstrapNodesV5(ctx, cfg) + + if ctx.GlobalIsSet(MaxPeersFlag.Name) { + cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) + } + if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) { + cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name) + } + if ctx.GlobalIsSet(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name) { + cfg.NoDiscovery = true } - // if we're running a light client or server, force enable the v5 peer discovery unless it is explicitly disabled with --nodiscover - // note that explicitly specifying --v5disc overrides --nodiscover, in which case the later only disables v4 discovery + // if we're running a light client or server, force enable the v5 peer discovery + // unless it is explicitly disabled with --nodiscover note that explicitly specifying + // --v5disc overrides --nodiscover, in which case the later only disables v4 discovery forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name) - - config := &node.Config{ - DataDir: MakeDataDir(ctx), - KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name), - UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name), - PrivateKey: MakeNodeKey(ctx), - Name: name, - Version: vsn, - UserIdent: makeNodeUserIdent(ctx), - NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // always disable v4 discovery in light client mode - DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || forceV5Discovery, - DiscoveryV5Addr: MakeDiscoveryV5Address(ctx), - BootstrapNodes: MakeBootstrapNodes(ctx), - BootstrapNodesV5: MakeBootstrapNodesV5(ctx), - ListenAddr: MakeListenAddress(ctx), - NAT: MakeNAT(ctx), - MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), - MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name), - IPCPath: MakeIPCPath(ctx), - HTTPHost: MakeHTTPRpcHost(ctx), - HTTPPort: ctx.GlobalInt(RPCPortFlag.Name), - HTTPCors: ctx.GlobalString(RPCCORSDomainFlag.Name), - HTTPModules: MakeRPCModules(ctx.GlobalString(RPCApiFlag.Name)), - WSHost: MakeWSRpcHost(ctx), - WSPort: ctx.GlobalInt(WSPortFlag.Name), - WSOrigins: ctx.GlobalString(WSAllowedOriginsFlag.Name), - WSModules: MakeRPCModules(ctx.GlobalString(WSApiFlag.Name)), - } - if ctx.GlobalBool(DevModeFlag.Name) { - if !ctx.GlobalIsSet(DataDirFlag.Name) { - config.DataDir = filepath.Join(os.TempDir(), "/ethereum_dev_mode") - } - // --dev mode does not need p2p networking. - config.MaxPeers = 0 - config.ListenAddr = ":0" + if ctx.GlobalIsSet(DiscoveryV5Flag.Name) { + cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name) + } else if forceV5Discovery { + cfg.DiscoveryV5 = true } + if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" { list, err := netutil.ParseNetlist(netrestrict) if err != nil { Fatalf("Option %q: %v", NetrestrictFlag.Name, err) } - config.NetRestrict = list + cfg.NetRestrict = list } - stack, err := node.New(config) - if err != nil { - Fatalf("Failed to create the protocol stack: %v", err) + if ctx.GlobalBool(DevModeFlag.Name) { + // --dev mode can't use p2p networking. + cfg.MaxPeers = 0 + cfg.ListenAddr = ":0" + cfg.NoDiscovery = true + cfg.DiscoveryV5 = false } - return stack } -// RegisterEthService configures eth.Ethereum from command line flags and adds it to the -// given node. -func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { - // Avoid conflicting network flags - networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag} - for _, flag := range netFlags { - if ctx.GlobalBool(flag.Name) { - networks++ +// SetNodeConfig applies node-related command line flags to the config. +func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { + SetP2PConfig(ctx, &cfg.P2P) + setIPC(ctx, cfg) + setHTTP(ctx, cfg) + setWS(ctx, cfg) + setNodeUserIdent(ctx, cfg) + + switch { + case ctx.GlobalIsSet(DataDirFlag.Name): + cfg.DataDir = ctx.GlobalString(DataDirFlag.Name) + case ctx.GlobalBool(DevModeFlag.Name): + cfg.DataDir = filepath.Join(os.TempDir(), "ethereum_dev_mode") + case ctx.GlobalBool(TestNetFlag.Name): + cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet") + } + + if ctx.GlobalIsSet(KeyStoreDirFlag.Name) { + cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name) + } + if ctx.GlobalIsSet(LightKDFFlag.Name) { + cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name) + } +} + +func setGPO(ctx *cli.Context, cfg *gasprice.Config) { + if ctx.GlobalIsSet(GpoBlocksFlag.Name) { + cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) + } + if ctx.GlobalIsSet(GpoPercentileFlag.Name) { + cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name) + } +} + +func setEthash(ctx *cli.Context, cfg *eth.Config) { + if ctx.GlobalIsSet(EthashCacheDirFlag.Name) { + cfg.EthashCacheDir = ctx.GlobalString(EthashCacheDirFlag.Name) + } + if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) { + cfg.EthashDatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name) + } + if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) { + cfg.EthashCachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name) + } + if ctx.GlobalIsSet(EthashCachesOnDiskFlag.Name) { + cfg.EthashCachesOnDisk = ctx.GlobalInt(EthashCachesOnDiskFlag.Name) + } + if ctx.GlobalIsSet(EthashDatasetsInMemoryFlag.Name) { + cfg.EthashDatasetsInMem = ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name) + } + if ctx.GlobalIsSet(EthashDatasetsOnDiskFlag.Name) { + cfg.EthashDatasetsOnDisk = ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name) + } +} + +func checkExclusive(ctx *cli.Context, flags ...cli.Flag) { + set := make([]string, 0, 1) + for _, flag := range flags { + if ctx.GlobalIsSet(flag.GetName()) { + set = append(set, "--"+flag.GetName()) } } - if networks > 1 { - Fatalf("The %v flags are mutually exclusive", netFlags) + if len(set) > 1 { + Fatalf("flags %v can't be used at the same time", strings.Join(set, ", ")) } +} + +// SetEthConfig applies eth-related command line flags to the config. +func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { + // Avoid conflicting network flags + checkExclusive(ctx, DevModeFlag, TestNetFlag) + checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag) + ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) + setEtherbase(ctx, ks, cfg) + setGPO(ctx, &cfg.GPO) + setEthash(ctx, cfg) + + switch { + case ctx.GlobalIsSet(SyncModeFlag.Name): + cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) + case ctx.GlobalBool(FastSyncFlag.Name): + cfg.SyncMode = downloader.FastSync + case ctx.GlobalBool(LightModeFlag.Name): + cfg.SyncMode = downloader.LightSync + } + if ctx.GlobalIsSet(LightServFlag.Name) { + cfg.LightServ = ctx.GlobalInt(LightServFlag.Name) + } + if ctx.GlobalIsSet(LightPeersFlag.Name) { + cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name) + } + if ctx.GlobalIsSet(NetworkIdFlag.Name) { + cfg.NetworkId = ctx.GlobalInt(NetworkIdFlag.Name) + } + + // Ethereum needs to know maxPeers to calculate the light server peer ratio. + // TODO(fjl): ensure Ethereum can get MaxPeers from node. + cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) + + if ctx.GlobalIsSet(CacheFlag.Name) { + cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) + } + cfg.DatabaseHandles = makeDatabaseHandles() + + if ctx.GlobalIsSet(MinerThreadsFlag.Name) { + cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name) + } + if ctx.GlobalIsSet(DocRootFlag.Name) { + cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name) + } + if ctx.GlobalIsSet(ExtraDataFlag.Name) { + cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name)) + } + if ctx.GlobalIsSet(GasPriceFlag.Name) { + cfg.GasPrice = GlobalBig(ctx, GasPriceFlag.Name) + } + if ctx.GlobalIsSet(VMEnableDebugFlag.Name) { + // TODO(fjl): force-enable this in --dev mode + cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) + } - ethConf := ð.Config{ - Etherbase: MakeEtherbase(ks, ctx), - ChainConfig: MakeChainConfig(ctx, stack), - FastSync: ctx.GlobalBool(FastSyncFlag.Name), - LightMode: ctx.GlobalBool(LightModeFlag.Name), - LightServ: ctx.GlobalInt(LightServFlag.Name), - LightPeers: ctx.GlobalInt(LightPeersFlag.Name), - MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), - DatabaseCache: ctx.GlobalInt(CacheFlag.Name), - DatabaseHandles: MakeDatabaseHandles(), - NetworkId: ctx.GlobalInt(NetworkIdFlag.Name), - MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), - ExtraData: MakeMinerExtra(extra, ctx), - DocRoot: ctx.GlobalString(DocRootFlag.Name), - GasPrice: GlobalBig(ctx, GasPriceFlag.Name), - GpoMinGasPrice: GlobalBig(ctx, GpoMinGasPriceFlag.Name), - GpoMaxGasPrice: GlobalBig(ctx, GpoMaxGasPriceFlag.Name), - GpoFullBlockRatio: ctx.GlobalInt(GpoFullBlockRatioFlag.Name), - GpobaseStepDown: ctx.GlobalInt(GpobaseStepDownFlag.Name), - GpobaseStepUp: ctx.GlobalInt(GpobaseStepUpFlag.Name), - GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name), - SolcPath: ctx.GlobalString(SolcPathFlag.Name), - EthashCacheDir: MakeEthashCacheDir(ctx), - EthashCachesInMem: ctx.GlobalInt(EthashCachesInMemoryFlag.Name), - EthashCachesOnDisk: ctx.GlobalInt(EthashCachesOnDiskFlag.Name), - EthashDatasetDir: MakeEthashDatasetDir(ctx), - EthashDatasetsInMem: ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name), - EthashDatasetsOnDisk: ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name), - EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name), - } - - // Override any default configs in dev mode or the test net + // Override any default configs for --dev and --testnet. switch { case ctx.GlobalBool(TestNetFlag.Name): if !ctx.GlobalIsSet(NetworkIdFlag.Name) { - ethConf.NetworkId = 3 + cfg.NetworkId = 3 } - ethConf.Genesis = core.DefaultTestnetGenesisBlock() - + cfg.Genesis = core.DefaultTestnetGenesisBlock() case ctx.GlobalBool(DevModeFlag.Name): - ethConf.Genesis = core.DevGenesisBlock() + cfg.Genesis = core.DevGenesisBlock() if !ctx.GlobalIsSet(GasPriceFlag.Name) { - ethConf.GasPrice = new(big.Int) + cfg.GasPrice = new(big.Int) } - ethConf.PowTest = true + cfg.PowTest = true } - // Override any global options pertaining to the Ethereum protocol + + // TODO(fjl): move trie cache generations into config if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 { state.MaxTrieCacheGen = uint16(gen) } +} - if ethConf.LightMode { - if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return les.New(ctx, ethConf) - }); err != nil { - Fatalf("Failed to register the Ethereum light node service: %v", err) - } +// RegisterEthService adds an Ethereum client to the stack. +func RegisterEthService(stack *node.Node, cfg *eth.Config) { + var err error + if cfg.SyncMode == downloader.LightSync { + err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return les.New(ctx, cfg) + }) } else { - if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - fullNode, err := eth.New(ctx, ethConf) - if fullNode != nil && ethConf.LightServ > 0 { - ls, _ := les.NewLesServer(fullNode, ethConf) + err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + fullNode, err := eth.New(ctx, cfg) + if fullNode != nil && cfg.LightServ > 0 { + ls, _ := les.NewLesServer(fullNode, cfg) fullNode.AddLesServer(ls) } return fullNode, err - }); err != nil { - Fatalf("Failed to register the Ethereum full node service: %v", err) - } + }) + } + if err != nil { + Fatalf("Failed to register the Ethereum service: %v", err) } } @@ -881,70 +897,10 @@ func RegisterEthStatsService(stack *node.Node, url string) { // SetupNetwork configures the system for either the main net or some test network. func SetupNetwork(ctx *cli.Context) { + // TODO(fjl): move target gas limit into config params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name)) } -// MakeChainConfig reads the chain configuration from the database in ctx.Datadir. -func MakeChainConfig(ctx *cli.Context, stack *node.Node) *params.ChainConfig { - db := MakeChainDatabase(ctx, stack) - defer db.Close() - - return MakeChainConfigFromDb(ctx, db) -} - -// MakeChainConfigFromDb reads the chain configuration from the given database. -func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig { - // If the chain is already initialized, use any existing chain configs - config := new(params.ChainConfig) - - genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0) - if genesis != nil { - storedConfig, err := core.GetChainConfig(db, genesis.Hash()) - switch err { - case nil: - config = storedConfig - case core.ChainConfigNotFoundErr: - // No configs found, use empty, will populate below - default: - Fatalf("Could not make chain configuration: %v", err) - } - } - // set chain id in case it's zero. - if config.ChainId == nil { - config.ChainId = new(big.Int) - } - // Check whether we are allowed to set default config params or not: - // - If no genesis is set, we're running either mainnet or testnet (private nets use `geth init`) - // - If a genesis is already set, ensure we have a configuration for it (mainnet or testnet) - defaults := genesis == nil || - (genesis.Hash() == params.MainNetGenesisHash && !ctx.GlobalBool(TestNetFlag.Name)) || - (genesis.Hash() == params.TestNetGenesisHash && ctx.GlobalBool(TestNetFlag.Name)) - - if defaults { - if ctx.GlobalBool(TestNetFlag.Name) { - config = params.TestnetChainConfig - } else if ctx.GlobalBool(DevModeFlag.Name) { - config = params.AllProtocolChanges - } else { - // Homestead fork - config.HomesteadBlock = params.MainNetHomesteadBlock - // DAO fork - config.DAOForkBlock = params.MainNetDAOForkBlock - config.DAOForkSupport = true - - // DoS reprice fork - config.EIP150Block = params.MainNetHomesteadGasRepriceBlock - config.EIP150Hash = params.MainNetHomesteadGasRepriceHash - - // DoS state cleanup fork - config.EIP155Block = params.MainNetSpuriousDragon - config.EIP158Block = params.MainNetSpuriousDragon - config.ChainId = params.MainNetChainID - } - } - return config -} - func ChainDbName(ctx *cli.Context) string { if ctx.GlobalBool(LightModeFlag.Name) { return "lightchaindata" @@ -957,7 +913,7 @@ func ChainDbName(ctx *cli.Context) string { func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { var ( cache = ctx.GlobalInt(CacheFlag.Name) - handles = MakeDatabaseHandles() + handles = makeDatabaseHandles() name = ChainDbName(ctx) ) @@ -968,26 +924,34 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { return chainDb } +func MakeGenesis(ctx *cli.Context) *core.Genesis { + var genesis *core.Genesis + switch { + case ctx.GlobalBool(TestNetFlag.Name): + genesis = core.DefaultTestnetGenesisBlock() + case ctx.GlobalBool(DevModeFlag.Name): + genesis = core.DevGenesisBlock() + } + return genesis +} + // MakeChain creates a chain manager from set command line flags. func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) { var err error chainDb = MakeChainDatabase(ctx, stack) - if ctx.GlobalBool(TestNetFlag.Name) { - _, err := core.WriteTestNetGenesisBlock(chainDb) - if err != nil { - Fatalf("Failed to write testnet genesis: %v", err) - } - } - chainConfig := MakeChainConfigFromDb(ctx, chainDb) - - seal := pow.PoW(pow.FakePow{}) + engine := ethash.NewFaker() if !ctx.GlobalBool(FakePoWFlag.Name) { - seal = pow.NewFullEthash("", 1, 0, "", 1, 0) + engine = ethash.New("", 1, 0, "", 1, 0) + } + config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx)) + if err != nil { + Fatalf("%v", err) } - chain, err = core.NewBlockChain(chainDb, chainConfig, seal, new(event.TypeMux), vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}) + vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} + chain, err = core.NewBlockChain(chainDb, config, engine, new(event.TypeMux), vmcfg) if err != nil { - Fatalf("Could not start chainmanager: %v", err) + Fatalf("Can't create BlockChain: %v", err) } return chain, chainDb } diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 82d7eda3c..b40352f57 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -27,7 +27,9 @@ import ( "encoding/hex" "flag" "fmt" + "io/ioutil" "os" + "path/filepath" "strconv" "strings" "time" @@ -46,7 +48,6 @@ import ( ) const quitCommand = "~Q" -const symKeyName = "da919ea33001b04dfc630522e33078ec0df11" // singletons var ( @@ -64,7 +65,8 @@ var ( pub *ecdsa.PublicKey asymKey *ecdsa.PrivateKey nodeid *ecdsa.PrivateKey - topic whisper.TopicType + topic []byte + asymKeyID string filterID string symPass string msPassword string @@ -72,27 +74,30 @@ var ( // cmd arguments var ( - echoMode = flag.Bool("e", false, "echo mode: prints some arguments for diagnostics") - bootstrapMode = flag.Bool("b", false, "boostrap node: don't actively connect to peers, wait for incoming connections") - forwarderMode = flag.Bool("f", false, "forwarder mode: only forward messages, neither send nor decrypt messages") - mailServerMode = flag.Bool("s", false, "mail server mode: delivers expired messages on demand") - requestMail = flag.Bool("r", false, "request expired messages from the bootstrap server") - asymmetricMode = flag.Bool("a", false, "use asymmetric encryption") - testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics") - generateKey = flag.Bool("k", false, "generate and show the private key") + bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't actively connect to peers, wait for incoming connections") + forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither send nor decrypt messages") + mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand") + requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server") + asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption") + generateKey = flag.Bool("generatekey", false, "generate and show the private key") + fileExMode = flag.Bool("fileexchange", false, "file exchange mode") + testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics") + echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics") argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level") argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") argWorkTime = flag.Uint("work", 5, "work time in seconds") - argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") - argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request") - - argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") - argPub = flag.String("pub", "", "public key for asymmetric encryption") - argDBPath = flag.String("dbpath", "", "path to the server's DB directory") - argIDFile = flag.String("idfile", "", "file name with node id (private key)") - argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") - argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") + argMaxSize = flag.Int("maxsize", whisper.DefaultMaxMessageLength, "max size of message") + argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") + argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request") + + argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") + argPub = flag.String("pub", "", "public key for asymmetric encryption") + argDBPath = flag.String("dbpath", "", "path to the server's DB directory") + argIDFile = flag.String("idfile", "", "file name with node id (private key)") + argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") + argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") + argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files") ) func main() { @@ -124,7 +129,7 @@ func processArgs() { if err != nil { utils.Fatalf("Failed to parse the topic: %s", err) } - topic = whisper.BytesToTopic(x) + topic = x } if *asymmetricMode && len(*argPub) > 0 { @@ -134,6 +139,14 @@ func processArgs() { } } + if len(*argSaveDir) > 0 { + if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) { + utils.Fatalf("Download directory '%s' does not exist", *argSaveDir) + } + } else if *fileExMode { + utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode") + } + if *echoMode { echo() } @@ -199,9 +212,40 @@ func initialize() { shh = whisper.New() } - asymKey = shh.NewIdentity() + if *argPoW != whisper.DefaultMinimumPoW { + err := shh.SetMinimumPoW(*argPoW) + if err != nil { + utils.Fatalf("Failed to set PoW: %s", err) + } + } + + if *argMaxSize != whisper.DefaultMaxMessageLength { + err := shh.SetMaxMessageLength(*argMaxSize) + if err != nil { + utils.Fatalf("Failed to set max message size: %s", err) + } + } + + asymKeyID, err = shh.NewKeyPair() + if err != nil { + utils.Fatalf("Failed to generate a new key pair: %s", err) + } + + asymKey, err = shh.GetPrivateKey(asymKeyID) + if err != nil { + utils.Fatalf("Failed to retrieve a new key pair: %s", err) + } + if nodeid == nil { - nodeid = shh.NewIdentity() + tmpID, err := shh.NewKeyPair() + if err != nil { + utils.Fatalf("Failed to generate a new key pair: %s", err) + } + + nodeid, err = shh.GetPrivateKey(tmpID) + if err != nil { + utils.Fatalf("Failed to retrieve a new key pair: %s", err) + } } maxPeers := 80 @@ -213,7 +257,7 @@ func initialize() { Config: p2p.Config{ PrivateKey: nodeid, MaxPeers: maxPeers, - Name: common.MakeName("whisper-go", "5.0"), + Name: common.MakeName("wnode", "5.0"), Protocols: shh.Protocols(), ListenAddr: *argIP, NAT: nat.Any(), @@ -288,8 +332,14 @@ func configureNode() { } } - shh.AddSymKey(symKeyName, []byte(symPass)) - symKey = shh.GetSymKey(symKeyName) + symKeyID, err := shh.AddSymKeyFromPassword(symPass) + if err != nil { + utils.Fatalf("Failed to create symmetric key: %s", err) + } + symKey, err = shh.GetSymKey(symKeyID) + if err != nil { + utils.Fatalf("Failed to save symmetric key: %s", err) + } if len(*argTopic) == 0 { generateTopic([]byte(symPass)) } @@ -302,12 +352,12 @@ func configureNode() { } filter := whisper.Filter{ - KeySym: symKey, - KeyAsym: asymKey, - Topics: []whisper.TopicType{topic}, - AcceptP2P: p2pAccept, + KeySym: symKey, + KeyAsym: asymKey, + Topics: [][]byte{topic}, + AllowP2P: p2pAccept, } - filterID, err = shh.Watch(&filter) + filterID, err = shh.Subscribe(&filter) if err != nil { utils.Fatalf("Failed to install filter: %s", err) } @@ -351,6 +401,8 @@ func run() { if *requestMail { requestExpiredMessagesLoop() + } else if *fileExMode { + sendFilesLoop() } else { sendLoop() } @@ -376,6 +428,31 @@ func sendLoop() { } } +func sendFilesLoop() { + for { + s := scanLine("") + if s == quitCommand { + fmt.Println("Quit command received") + close(done) + break + } + b, err := ioutil.ReadFile(s) + if err != nil { + fmt.Printf(">>> Error: %s \n", err) + continue + } else { + h := sendMsg(b) + if (h == common.Hash{}) { + fmt.Printf(">>> Error: message was not sent \n") + } else { + timestamp := time.Now().Unix() + from := crypto.PubkeyToAddress(asymKey.PublicKey) + fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h) + } + } + } +} + func scanLine(prompt string) string { if len(prompt) > 0 { fmt.Print(prompt) @@ -402,29 +479,36 @@ func scanUint(prompt string) uint32 { return uint32(i) } -func sendMsg(payload []byte) { +func sendMsg(payload []byte) common.Hash { params := whisper.MessageParams{ Src: asymKey, Dst: pub, KeySym: symKey, Payload: payload, - Topic: topic, + Topic: whisper.BytesToTopic(topic), TTL: uint32(*argTTL), PoW: *argPoW, WorkTime: uint32(*argWorkTime), } msg := whisper.NewSentMessage(¶ms) + if msg == nil { + fmt.Printf("failed to create new message (OS level error)") + os.Exit(0) + } envelope, err := msg.Wrap(¶ms) if err != nil { fmt.Printf("failed to seal message: %v \n", err) - return + return common.Hash{} } err = shh.Send(envelope) if err != nil { fmt.Printf("failed to send message: %v \n", err) + return common.Hash{} } + + return envelope.Hash() } func messageLoop() { @@ -440,7 +524,11 @@ func messageLoop() { case <-ticker.C: messages := f.Retrieve() for _, msg := range messages { - printMessageInfo(msg) + if *fileExMode || len(msg.Payload) > 2048 { + writeMessageToFile(*argSaveDir, msg) + } else { + printMessageInfo(msg) + } } case <-done: return @@ -464,19 +552,47 @@ func printMessageInfo(msg *whisper.ReceivedMessage) { } } +func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) { + timestamp := fmt.Sprintf("%d", msg.Sent) + name := fmt.Sprintf("%x", msg.EnvelopeHash) + + var address common.Address + if msg.Src != nil { + address = crypto.PubkeyToAddress(*msg.Src) + } + + if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { + // message from myself: don't save, only report + fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name) + } else if len(dir) > 0 { + fullpath := filepath.Join(dir, name) + err := ioutil.WriteFile(fullpath, msg.Payload, 0644) + if err != nil { + fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err) + } else { + fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Payload)) + } + } else { + fmt.Printf("\n%s {%x}: big message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Payload), name) + } +} + func requestExpiredMessagesLoop() { var key, peerID []byte var timeLow, timeUpp uint32 var t string var xt, empty whisper.TopicType - err := shh.AddSymKey(mailserver.MailServerKeyName, []byte(msPassword)) + keyID, err := shh.AddSymKeyFromPassword(msPassword) if err != nil { utils.Fatalf("Failed to create symmetric key for mail request: %s", err) } - key = shh.GetSymKey(mailserver.MailServerKeyName) + key, err = shh.GetSymKey(keyID) + if err != nil { + utils.Fatalf("Failed to save symmetric key for mail request: %s", err) + } peerID = extractIdFromEnode(*argEnode) - shh.MarkPeerTrusted(peerID) + shh.AllowP2PMessagesFromPeer(peerID) for { timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") @@ -509,6 +625,9 @@ func requestExpiredMessagesLoop() { params.WorkTime = 5 msg := whisper.NewSentMessage(¶ms) + if msg == nil { + utils.Fatalf("failed to create new message (OS level error)") + } env, err := msg.Wrap(¶ms) if err != nil { utils.Fatalf("Wrap failed: %s", err) @@ -527,7 +646,6 @@ func extractIdFromEnode(s string) []byte { n, err := discover.ParseNode(s) if err != nil { utils.Fatalf("Failed to parse enode: %s", err) - return nil } return n.ID[:] } diff --git a/common/hexutil/json.go b/common/hexutil/json.go index 23393ed2c..1bc1d014c 100644 --- a/common/hexutil/json.go +++ b/common/hexutil/json.go @@ -51,7 +51,7 @@ func (b *Bytes) UnmarshalJSON(input []byte) error { // UnmarshalText implements encoding.TextUnmarshaler. func (b *Bytes) UnmarshalText(input []byte) error { - raw, err := checkText(input) + raw, err := checkText(input, true) if err != nil { return err } @@ -73,7 +73,28 @@ func (b Bytes) String() string { // determines the required input length. This function is commonly used to implement the // UnmarshalText method for fixed-size types. func UnmarshalFixedText(typname string, input, out []byte) error { - raw, err := checkText(input) + raw, err := checkText(input, true) + if err != nil { + return err + } + if len(raw)/2 != len(out) { + return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) + } + // Pre-verify syntax before modifying out. + for _, b := range raw { + if decodeNibble(b) == badNibble { + return ErrSyntax + } + } + hex.Decode(out, raw) + return nil +} + +// UnmarshalFixedUnprefixedText decodes the input as a string with optional 0x prefix. The +// length of out determines the required input length. This function is commonly used to +// implement the UnmarshalText method for fixed-size types. +func UnmarshalFixedUnprefixedText(typname string, input, out []byte) error { + raw, err := checkText(input, false) if err != nil { return err } @@ -243,14 +264,15 @@ func bytesHave0xPrefix(input []byte) bool { return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') } -func checkText(input []byte) ([]byte, error) { +func checkText(input []byte, wantPrefix bool) ([]byte, error) { if len(input) == 0 { return nil, nil // empty strings are allowed } - if !bytesHave0xPrefix(input) { + if bytesHave0xPrefix(input) { + input = input[2:] + } else if wantPrefix { return nil, ErrMissingPrefix } - input = input[2:] if len(input)%2 != 0 { return nil, ErrOddLength } diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go index af7f44915..e4e827491 100644 --- a/common/hexutil/json_test.go +++ b/common/hexutil/json_test.go @@ -337,3 +337,38 @@ func TestUnmarshalUint(t *testing.T) { } } } + +func TestUnmarshalFixedUnprefixedText(t *testing.T) { + tests := []struct { + input string + want []byte + wantErr error + }{ + {input: "0x2", wantErr: ErrOddLength}, + {input: "2", wantErr: ErrOddLength}, + {input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")}, + {input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")}, + // check that output is not modified for partially correct input + {input: "444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}}, + {input: "0x444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}}, + // valid inputs + {input: "44444444", want: []byte{0x44, 0x44, 0x44, 0x44}}, + {input: "0x44444444", want: []byte{0x44, 0x44, 0x44, 0x44}}, + } + + for _, test := range tests { + out := make([]byte, 4) + err := UnmarshalFixedUnprefixedText("x", []byte(test.input), out) + switch { + case err == nil && test.wantErr != nil: + t.Errorf("%q: got no error, expected %q", test.input, test.wantErr) + case err != nil && test.wantErr == nil: + t.Errorf("%q: unexpected error %q", test.input, err) + case err != nil && err.Error() != test.wantErr.Error(): + t.Errorf("%q: error mismatch: got %q, want %q", test.input, err, test.wantErr) + } + if test.want != nil && !bytes.Equal(out, test.want) { + t.Errorf("%q: output mismatch: got %x, want %x", test.input, out, test.want) + } + } +} diff --git a/common/math/big.go b/common/math/big.go index 704ca40a9..5255a88e9 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -18,6 +18,7 @@ package math import ( + "fmt" "math/big" ) @@ -35,6 +36,27 @@ const ( wordBytes = wordBits / 8 ) +// HexOrDecimal256 marshals big.Int as hex or decimal. +type HexOrDecimal256 big.Int + +// UnmarshalText implements encoding.TextUnmarshaler. +func (i *HexOrDecimal256) UnmarshalText(input []byte) error { + bigint, ok := ParseBig256(string(input)) + if !ok { + return fmt.Errorf("invalid hex or decimal integer %q", input) + } + *i = HexOrDecimal256(*bigint) + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (i *HexOrDecimal256) MarshalText() ([]byte, error) { + if i == nil { + return []byte("0x0"), nil + } + return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil +} + // ParseBig256 parses s as a 256 bit integer in decimal or hexadecimal syntax. // Leading zeros are accepted. The empty string parses as zero. func ParseBig256(s string) (*big.Int, bool) { diff --git a/common/math/big_test.go b/common/math/big_test.go index 6eb13f4f1..deff25465 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -23,7 +23,7 @@ import ( "testing" ) -func TestParseBig256(t *testing.T) { +func TestHexOrDecimal256(t *testing.T) { tests := []struct { input string num *big.Int @@ -47,13 +47,14 @@ func TestParseBig256(t *testing.T) { {"115792089237316195423570985008687907853269984665640564039457584007913129639936", nil, false}, } for _, test := range tests { - num, ok := ParseBig256(test.input) - if ok != test.ok { - t.Errorf("ParseBig(%q) -> ok = %t, want %t", test.input, ok, test.ok) + var num HexOrDecimal256 + err := num.UnmarshalText([]byte(test.input)) + if (err == nil) != test.ok { + t.Errorf("ParseBig(%q) -> (err == nil) == %t, want %t", test.input, err == nil, test.ok) continue } - if num != nil && test.num != nil && num.Cmp(test.num) != 0 { - t.Errorf("ParseBig(%q) -> %d, want %d", test.input, num, test.num) + if test.num != nil && (*big.Int)(&num).Cmp(test.num) != 0 { + t.Errorf("ParseBig(%q) -> %d, want %d", test.input, (*big.Int)(&num), test.num) } } } diff --git a/common/math/integer.go b/common/math/integer.go index a3eeee27e..7eff4d3b0 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -16,7 +16,10 @@ package math -import "strconv" +import ( + "fmt" + "strconv" +) const ( // Integer limit values. @@ -34,6 +37,24 @@ const ( MaxUint64 = 1<<64 - 1 ) +// HexOrDecimal64 marshals uint64 as hex or decimal. +type HexOrDecimal64 uint64 + +// UnmarshalText implements encoding.TextUnmarshaler. +func (i *HexOrDecimal64) UnmarshalText(input []byte) error { + int, ok := ParseUint64(string(input)) + if !ok { + return fmt.Errorf("invalid hex or decimal integer %q", input) + } + *i = HexOrDecimal64(int) + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (i HexOrDecimal64) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("%#x", uint64(i))), nil +} + // ParseUint64 parses s as an integer in decimal or hexadecimal syntax. // Leading zeros are accepted. The empty string parses as zero. func ParseUint64(s string) (uint64, bool) { diff --git a/common/math/integer_test.go b/common/math/integer_test.go index 05bba221f..b31c7c26c 100644 --- a/common/math/integer_test.go +++ b/common/math/integer_test.go @@ -65,7 +65,7 @@ func TestOverflow(t *testing.T) { } } -func TestParseUint64(t *testing.T) { +func TestHexOrDecimal64(t *testing.T) { tests := []struct { input string num uint64 @@ -88,12 +88,13 @@ func TestParseUint64(t *testing.T) { {"18446744073709551617", 0, false}, } for _, test := range tests { - num, ok := ParseUint64(test.input) - if ok != test.ok { - t.Errorf("ParseUint64(%q) -> ok = %t, want %t", test.input, ok, test.ok) + var num HexOrDecimal64 + err := num.UnmarshalText([]byte(test.input)) + if (err == nil) != test.ok { + t.Errorf("ParseUint64(%q) -> (err == nil) = %t, want %t", test.input, err == nil, test.ok) continue } - if ok && num != test.num { + if err == nil && uint64(num) != test.num { t.Errorf("ParseUint64(%q) -> %d, want %d", test.input, num, test.num) } } diff --git a/common/types.go b/common/types.go index 9c50beb13..05288bf46 100644 --- a/common/types.go +++ b/common/types.go @@ -17,6 +17,7 @@ package common import ( + "encoding/hex" "fmt" "math/big" "math/rand" @@ -30,13 +31,8 @@ const ( AddressLength = 20 ) -type ( - // Hash represents the 32 byte Keccak256 hash of arbitrary data. - Hash [HashLength]byte - - // Address represents the 20 byte address of an Ethereum account. - Address [AddressLength]byte -) +// Hash represents the 32 byte Keccak256 hash of arbitrary data. +type Hash [HashLength]byte func BytesToHash(b []byte) Hash { var h Hash @@ -113,7 +109,24 @@ func EmptyHash(h Hash) bool { return h == Hash{} } +// UnprefixedHash allows marshaling a Hash without 0x prefix. +type UnprefixedHash Hash + +// UnmarshalText decodes the hash from hex. The 0x prefix is optional. +func (h *UnprefixedHash) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedUnprefixedText("UnprefixedHash", input, h[:]) +} + +// MarshalText encodes the hash as hex. +func (h UnprefixedHash) MarshalText() ([]byte, error) { + return []byte(hex.EncodeToString(h[:])), nil +} + /////////// Address + +// Address represents the 20 byte address of an Ethereum account. +type Address [AddressLength]byte + func BytesToAddress(b []byte) Address { var a Address a.SetBytes(b) @@ -181,12 +194,15 @@ func (a *Address) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("Address", input, a[:]) } -// PP Pretty Prints a byte slice in the following format: -// hex(value[:4])...(hex[len(value)-4:]) -func PP(value []byte) string { - if len(value) <= 8 { - return Bytes2Hex(value) - } +// UnprefixedHash allows marshaling an Address without 0x prefix. +type UnprefixedAddress Address + +// UnmarshalText decodes the address from hex. The 0x prefix is optional. +func (a *UnprefixedAddress) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedUnprefixedText("UnprefixedAddress", input, a[:]) +} - return fmt.Sprintf("%x...%x", value[:4], value[len(value)-4]) +// MarshalText encodes the address as hex. +func (a UnprefixedAddress) MarshalText() ([]byte, error) { + return []byte(hex.EncodeToString(a[:])), nil } diff --git a/consensus/clique/api.go b/consensus/clique/api.go new file mode 100644 index 000000000..b875eef01 --- /dev/null +++ b/consensus/clique/api.go @@ -0,0 +1,119 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package clique + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" +) + +// API is a user facing RPC API to allow controlling the signer and voting +// mechanisms of the proof-of-authority scheme. +type API struct { + chain consensus.ChainReader + clique *Clique +} + +// GetSnapshot retrieves the state snapshot at a given block. +func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) { + // Retrieve the requested block number (or current if none requested) + var header *types.Header + if number == nil || *number == rpc.LatestBlockNumber { + header = api.chain.CurrentHeader() + } else { + header = api.chain.GetHeaderByNumber(uint64(number.Int64())) + } + // Ensure we have an actually valid block and return its snapshot + if header == nil { + return nil, errUnknownBlock + } + return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) +} + +// GetSnapshotAtHash retrieves the state snapshot at a given block. +func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) { + header := api.chain.GetHeaderByHash(hash) + if header == nil { + return nil, errUnknownBlock + } + return api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) +} + +// GetSigners retrieves the list of authorized signers at the specified block. +func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) { + // Retrieve the requested block number (or current if none requested) + var header *types.Header + if number == nil || *number == rpc.LatestBlockNumber { + header = api.chain.CurrentHeader() + } else { + header = api.chain.GetHeaderByNumber(uint64(number.Int64())) + } + // Ensure we have an actually valid block and return the signers from its snapshot + if header == nil { + return nil, errUnknownBlock + } + snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) + if err != nil { + return nil, err + } + return snap.signers(), nil +} + +// GetSignersAtHash retrieves the state snapshot at a given block. +func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) { + header := api.chain.GetHeaderByHash(hash) + if header == nil { + return nil, errUnknownBlock + } + snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil) + if err != nil { + return nil, err + } + return snap.signers(), nil +} + +// Proposals returns the current proposals the node tries to uphold and vote on. +func (api *API) Proposals() map[common.Address]bool { + api.clique.lock.RLock() + defer api.clique.lock.RUnlock() + + proposals := make(map[common.Address]bool) + for address, auth := range api.clique.proposals { + proposals[address] = auth + } + return proposals +} + +// Propose injects a new authorization proposal that the signer will attempt to +// push through. +func (api *API) Propose(address common.Address, auth bool) { + api.clique.lock.Lock() + defer api.clique.lock.Unlock() + + api.clique.proposals[address] = auth +} + +// Discard drops a currently running proposal, stopping the signer from casting +// further votes (either for or against). +func (api *API) Discard(address common.Address) { + api.clique.lock.Lock() + defer api.clique.lock.Unlock() + + delete(api.clique.proposals, address) +} diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go new file mode 100644 index 000000000..8619bd1d8 --- /dev/null +++ b/consensus/clique/clique.go @@ -0,0 +1,644 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package clique implements the proof-of-authority consensus engine. +package clique + +import ( + "bytes" + "errors" + "math/big" + "math/rand" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/sha3" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + lru "github.com/hashicorp/golang-lru" +) + +const ( + checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database + inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory + inmemorySignatures = 1024 // Number of recent blocks to keep in memory + + wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers +) + +// Clique proof-of-authority protocol constants. +var ( + epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes + blockPeriod = uint64(15) // Default minimum difference between two consecutive block's timestamps + + extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity + extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal + + nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer + nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer. + + uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW. + + diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures + diffNoTurn = big.NewInt(1) // Block difficulty for out-of-turn signatures +) + +// 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 ( + // errUnknownBlock is returned when the list of signers is requested for a block + // that is not part of the local blockchain. + errUnknownBlock = errors.New("unknown block") + + // errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition + // block has a beneficiary set to non zeroes. + errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero") + + // errInvalidVote is returned if a nonce value is something else that the two + // allowed constants of 0x00..0 or 0xff..f. + errInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f") + + // errInvalidCheckpointVote is returned if a checkpoint/epoch transition block + // has a vote nonce set to non zeroes. + errInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero") + + // errMissingVanity is returned if a block's extra-data section is shorter than + // 32 bytes, which is required to store the signer vanity. + errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing") + + // errMissingSignature is returned if a block's extra-data section doesn't seem + // to contain a 65 byte secp256k1 signature. + errMissingSignature = errors.New("extra-data 65 byte suffix signature missing") + + // errExtraSigners is returned if non-checkpoint block contain signer data in + // their extra-data fields. + errExtraSigners = errors.New("non-checkpoint block contains extra signer list") + + // drrInvalidCheckpointSigners is returned if a checkpoint block contains an + // invalid list of signers (i.e. non divisible by 20 bytes, or not the correct + // ones). + drrInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block") + + // errInvalidMixDigest is returned if a block's mix digest is non zero. + errInvalidMixDigest = errors.New("non-zero mix digest") + + // errInvalidUncleHash is returned if a block contains an non-empty uncle list. + errInvalidUncleHash = errors.New("non empty uncle hash") + + // errInvalidDifficulty is returned if the difficulty of a block is not either + // of 1 or 2, or if the value does not match the turn of the signer. + errInvalidDifficulty = errors.New("invalid difficulty") + + // ErrInvalidTimestamp is returned if the timestamp of a block is lower than + // the previous block's timestamp + the minimum block period. + ErrInvalidTimestamp = errors.New("invalid timestamp") + + // errInvalidVotingChain is returned if an authorization list is attempted to + // be modified via out-of-range or non-contiguous headers. + errInvalidVotingChain = errors.New("invalid voting chain") + + // errUnauthorized is returned if a header is signed by a non authorized entity. + errUnauthorized = errors.New("unauthorized") +) + +// SignerFn is a signer callback function to request a hash to be signed by a +// backing account. +type SignerFn func(accounts.Account, []byte) ([]byte, error) + +// sigHash returns the hash which is used as input for the proof-of-authority +// signing. It is the hash of the entire header apart from the 65 byte signature +// contained at the end of the extra data. +// +// Note, the method requires the extra data to be at least 65 bytes, otherwise it +// panics. This is done to avoid accidentally using both forms (signature present +// or not), which could be abused to produce different hashes for the same header. +func sigHash(header *types.Header) (hash common.Hash) { + hasher := sha3.NewKeccak256() + + rlp.Encode(hasher, []interface{}{ + header.ParentHash, + header.UncleHash, + header.Coinbase, + header.Root, + header.TxHash, + header.ReceiptHash, + header.Bloom, + header.Difficulty, + header.Number, + header.GasLimit, + header.GasUsed, + header.Time, + header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short + header.MixDigest, + header.Nonce, + }) + hasher.Sum(hash[:0]) + return hash +} + +// ecrecover extracts the Ethereum account address from a signed header. +func ecrecover(header *types.Header) (common.Address, error) { + // Retrieve the signature from the header extra-data + if len(header.Extra) < extraSeal { + return common.Address{}, errMissingSignature + } + signature := header.Extra[len(header.Extra)-extraSeal:] + + // Recover the public key and the Ethereum address + pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature) + if err != nil { + return common.Address{}, err + } + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + + return signer, nil +} + +// Clique is the proof-of-authority consensus engine proposed to support the +// Ethereum testnet following the Ropsten attacks. +type Clique struct { + config *params.CliqueConfig // Consensus engine configuration parameters + db ethdb.Database // Database to store and retrieve snapshot checkpoints + + recents *lru.ARCCache // Snapshots for recent block to speed up reorgs + signatures *lru.ARCCache // Signatures of recent blocks to speed up mining + + proposals map[common.Address]bool // Current list of proposals we are pushing + + signer common.Address // Ethereum address of the signing key + signFn SignerFn // Signer function to authorize hashes with + lock sync.RWMutex // Protects the signer fields +} + +// New creates a Clique proof-of-authority consensus engine with the initial +// signers set to the ones provided by the user. +func New(config *params.CliqueConfig, db ethdb.Database) *Clique { + // Set any missing consensus parameters to their defaults + conf := *config + if conf.Epoch == 0 { + conf.Epoch = epochLength + } + if conf.Period == 0 { + conf.Period = blockPeriod + } + // Allocate the snapshot caches and create the engine + recents, _ := lru.NewARC(inmemorySnapshots) + signatures, _ := lru.NewARC(inmemorySignatures) + + return &Clique{ + config: &conf, + db: db, + recents: recents, + signatures: signatures, + proposals: make(map[common.Address]bool), + } +} + +// Author implements consensus.Engine, returning the Ethereum address recovered +// from the signature in the header's extra-data section. +func (c *Clique) Author(header *types.Header) (common.Address, error) { + return ecrecover(header) +} + +// VerifyHeader checks whether a header conforms to the consensus rules. +func (c *Clique) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { + return c.verifyHeader(chain, header, nil) +} + +// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The +// method returns a quit channel to abort the operations and a results channel to +// retrieve the async verifications (the order is that of the input slice). +func (c *Clique) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { + abort := make(chan struct{}) + results := make(chan error, len(headers)) + + go func() { + for i, header := range headers { + err := c.verifyHeader(chain, header, headers[:i]) + + select { + case <-abort: + return + case results <- err: + } + } + }() + return abort, results +} + +// verifyHeader checks whether a header conforms to the consensus rules.The +// caller may optionally pass in a batch of parents (ascending order) to avoid +// looking those up from the database. This is useful for concurrently verifying +// a batch of new headers. +func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { + if header.Number == nil { + return errUnknownBlock + } + number := header.Number.Uint64() + + // Don't waste time checking blocks from the future + if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { + return consensus.ErrFutureBlock + } + // Checkpoint blocks need to enforce zero beneficiary + checkpoint := (number % c.config.Epoch) == 0 + if checkpoint && header.Coinbase != (common.Address{}) { + return errInvalidCheckpointBeneficiary + } + // Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints + if !bytes.Equal(header.Nonce[:], nonceAuthVote) && !bytes.Equal(header.Nonce[:], nonceDropVote) { + return errInvalidVote + } + if checkpoint && !bytes.Equal(header.Nonce[:], nonceDropVote) { + return errInvalidCheckpointVote + } + // Check that the extra-data contains both the vanity and signature + if len(header.Extra) < extraVanity { + return errMissingVanity + } + if len(header.Extra) < extraVanity+extraSeal { + return errMissingSignature + } + // Ensure that the extra-data contains a signer list on checkpoint, but none otherwise + signersBytes := len(header.Extra) - extraVanity - extraSeal + if !checkpoint && signersBytes != 0 { + return errExtraSigners + } + if checkpoint && signersBytes%common.AddressLength != 0 { + return drrInvalidCheckpointSigners + } + // Ensure that the mix digest is zero as we don't have fork protection currently + if header.MixDigest != (common.Hash{}) { + return errInvalidMixDigest + } + // Ensure that the block doesn't contain any uncles which are meaningless in PoA + if header.UncleHash != uncleHash { + return errInvalidUncleHash + } + // Ensure that the block's difficulty is meaningful (may not be correct at this point) + if number > 0 { + if header.Difficulty == nil || (header.Difficulty.Cmp(diffInTurn) != 0 && header.Difficulty.Cmp(diffNoTurn) != 0) { + return errInvalidDifficulty + } + } + // All basic checks passed, verify cascading fields + return c.verifyCascadingFields(chain, header, parents) +} + +// verifyCascadingFields verifies all the header fields that are not standalone, +// rather depend on a batch of previous headers. The caller may optionally pass +// in a batch of parents (ascending order) to avoid looking those up from the +// database. This is useful for concurrently verifying a batch of new headers. +func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { + // The genesis block is the always valid dead-end + number := header.Number.Uint64() + if number == 0 { + return nil + } + // Ensure that the block's timestamp isn't too close to it's parent + var parent *types.Header + if len(parents) > 0 { + parent = parents[len(parents)-1] + } else { + parent = chain.GetHeader(header.ParentHash, number-1) + } + if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { + return consensus.ErrUnknownAncestor + } + if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() { + return ErrInvalidTimestamp + } + // Retrieve the snapshot needed to verify this header and cache it + snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) + if err != nil { + return err + } + // If the block is a checkpoint block, verify the signer list + if number%c.config.Epoch == 0 { + signers := make([]byte, len(snap.Signers)*common.AddressLength) + for i, signer := range snap.signers() { + copy(signers[i*common.AddressLength:], signer[:]) + } + extraSuffix := len(header.Extra) - extraSeal + if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) { + return drrInvalidCheckpointSigners + } + } + // All basic checks passed, verify the seal and return + return c.verifySeal(chain, header, parents) +} + +// snapshot retrieves the authorization snapshot at a given point in time. +func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) { + // Search for a snapshot in memory or on disk for checkpoints + var ( + headers []*types.Header + snap *Snapshot + ) + for snap == nil { + // If an in-memory snapshot was found, use that + if s, ok := c.recents.Get(hash); ok { + snap = s.(*Snapshot) + break + } + // If an on-disk checkpoint snapshot can be found, use that + if number%checkpointInterval == 0 { + if s, err := loadSnapshot(c.config, c.db, hash); err == nil { + log.Trace("Loaded voting snapshot form disk", "number", number, "hash", hash) + snap = s + break + } + } + // If we're at block zero, make a snapshot + if number == 0 { + genesis := chain.GetHeaderByNumber(0) + if err := c.VerifyHeader(chain, genesis, false); err != nil { + return nil, err + } + signers := make([]common.Address, (len(genesis.Extra)-extraVanity-extraSeal)/common.AddressLength) + for i := 0; i < len(signers); i++ { + copy(signers[i][:], genesis.Extra[extraVanity+i*common.AddressLength:]) + } + snap = newSnapshot(c.config, 0, genesis.Hash(), signers) + if err := snap.store(c.db); err != nil { + return nil, err + } + log.Trace("Stored genesis voting snapshot to disk") + break + } + // No snapshot for this header, gather the header and move backward + var header *types.Header + if len(parents) > 0 { + // If we have explicit parents, pick from there (enforced) + header = parents[len(parents)-1] + if header.Hash() != hash || header.Number.Uint64() != number { + return nil, consensus.ErrUnknownAncestor + } + parents = parents[:len(parents)-1] + } else { + // No explicit parents (or no more left), reach out to the database + header = chain.GetHeader(hash, number) + if header == nil { + return nil, consensus.ErrUnknownAncestor + } + } + headers = append(headers, header) + number, hash = number-1, header.ParentHash + } + // Previous snapshot found, apply any pending headers on top of it + for i := 0; i < len(headers)/2; i++ { + headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] + } + snap, err := snap.apply(headers) + if err != nil { + return nil, err + } + c.recents.Add(snap.Hash, snap) + + // If we've generated a new checkpoint snapshot, save to disk + if snap.Number%checkpointInterval == 0 && len(headers) > 0 { + if err = snap.store(c.db); err != nil { + return nil, err + } + log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash) + } + return snap, err +} + +// VerifyUncles implements consensus.Engine, always returning an error for any +// uncles as this consensus mechanism doesn't permit uncles. +func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { + if len(block.Uncles()) > 0 { + return errors.New("uncles not allowed") + } + return nil +} + +// VerifySeal implements consensus.Engine, checking whether the signature contained +// in the header satisfies the consensus protocol requirements. +func (c *Clique) VerifySeal(chain consensus.ChainReader, header *types.Header) error { + return c.verifySeal(chain, header, nil) +} + +// verifySeal checks whether the signature contained in the header satisfies the +// consensus protocol requirements. The method accepts an optional list of parent +// headers that aren't yet part of the local blockchain to generate the snapshots +// from. +func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { + // Verifying the genesis block is not supported + number := header.Number.Uint64() + if number == 0 { + return errUnknownBlock + } + // Retrieve the snapshot needed to verify this header and cache it + snap, err := c.snapshot(chain, number-1, header.ParentHash, parents) + if err != nil { + return err + } + c.recents.Add(snap.Hash, snap) + + // Resolve the authorization key and check against signers + signer, err := ecrecover(header) + if err != nil { + return err + } + if _, ok := snap.Signers[signer]; !ok { + return errUnauthorized + } + for seen, recent := range snap.Recents { + if recent == signer { + // Signer is among recents, only fail if the current block doens't shift it out + if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit { + return errUnauthorized + } + } + } + // Ensure that the difficulty corresponts to the turn-ness of the signer + inturn := snap.inturn(header.Number.Uint64(), signer) + if inturn && header.Difficulty.Cmp(diffInTurn) != 0 { + return errInvalidDifficulty + } + if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 { + return errInvalidDifficulty + } + return nil +} + +// Prepare implements consensus.Engine, preparing all the consensus fields of the +// header for running the transactions on top. +func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) error { + // If the block isn't a checkpoint, cast a random vote (good enough fror now) + header.Coinbase = common.Address{} + header.Nonce = types.BlockNonce{} + + number := header.Number.Uint64() + if number%c.config.Epoch != 0 { + c.lock.RLock() + if len(c.proposals) > 0 { + addresses := make([]common.Address, 0, len(c.proposals)) + for address := range c.proposals { + addresses = append(addresses, address) + } + header.Coinbase = addresses[rand.Intn(len(addresses))] + if c.proposals[header.Coinbase] { + copy(header.Nonce[:], nonceAuthVote) + } else { + copy(header.Nonce[:], nonceDropVote) + } + } + c.lock.RUnlock() + } + // Assemble the voting snapshot and set the correct difficulty + snap, err := c.snapshot(chain, number-1, header.ParentHash, nil) + if err != nil { + return err + } + header.Difficulty = diffNoTurn + if snap.inturn(header.Number.Uint64(), c.signer) { + header.Difficulty = diffInTurn + } + // Ensure the extra data has all it's components + if len(header.Extra) < extraVanity { + header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) + } + header.Extra = header.Extra[:extraVanity] + + if number%c.config.Epoch == 0 { + for _, signer := range snap.signers() { + header.Extra = append(header.Extra, signer[:]...) + } + } + header.Extra = append(header.Extra, make([]byte, extraSeal)...) + + // Mix digest is reserved for now, set to empty + header.MixDigest = common.Hash{} + + // Ensure the timestamp has the correct delay + parent := chain.GetHeader(header.ParentHash, number-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } + header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period)) + if header.Time.Int64() < time.Now().Unix() { + header.Time = big.NewInt(time.Now().Unix()) + } + return nil +} + +// Finalize implements consensus.Engine, ensuring no uncles are set, nor block +// rewards given, and returns the final block. +func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { + // No block rewards in PoA, so the state remains as is and uncles are dropped + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.UncleHash = types.CalcUncleHash(nil) + + // Assemble and return the final block for sealing + return types.NewBlock(header, txs, nil, receipts), nil +} + +// Authorize injects a private key into the consensus engine to mint new blocks +// with. +func (c *Clique) Authorize(signer common.Address, signFn SignerFn) { + c.lock.Lock() + defer c.lock.Unlock() + + c.signer = signer + c.signFn = signFn +} + +// Seal implements consensus.Engine, attempting to create a sealed block using +// the local signing credentials. +func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) { + header := block.Header() + + // Sealing the genesis block is not supported + number := header.Number.Uint64() + if number == 0 { + return nil, errUnknownBlock + } + // Don't hold the signer fields for the entire sealing procedure + c.lock.RLock() + signer, signFn := c.signer, c.signFn + c.lock.RUnlock() + + // Bail out if we're unauthorized to sign a block + snap, err := c.snapshot(chain, number-1, header.ParentHash, nil) + if err != nil { + return nil, err + } + if _, authorized := snap.Signers[signer]; !authorized { + return nil, errUnauthorized + } + // If we're amongs the recent signers, wait for the next block + for seen, recent := range snap.Recents { + if recent == signer { + // Signer is among recents, only wait if the current block doens't shift it out + if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit { + log.Info("Signed recently, must wait for others") + <-stop + return nil, nil + } + } + } + // Sweet, the protocol permits us to sign the block, wait for our time + delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) + if header.Difficulty.Cmp(diffNoTurn) == 0 { + // It's not our turn explicitly to sign, delay it a bit + wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime + delay += time.Duration(rand.Int63n(int64(wiggle))) + + log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle)) + } + log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay)) + + select { + case <-stop: + return nil, nil + case <-time.After(delay): + } + // Sign all the things! + sighash, err := signFn(accounts.Account{Address: signer}, sigHash(header).Bytes()) + if err != nil { + return nil, err + } + copy(header.Extra[len(header.Extra)-extraSeal:], sighash) + + return block.WithSeal(header), nil +} + +// APIs implements consensus.Engine, returning the user facing RPC API to allow +// controlling the signer voting. +func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API { + return []rpc.API{{ + Namespace: "clique", + Version: "1.0", + Service: &API{chain: chain, clique: c}, + Public: false, + }} +} diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go new file mode 100644 index 000000000..46b32ca5f --- /dev/null +++ b/consensus/clique/snapshot.go @@ -0,0 +1,299 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package clique + +import ( + "bytes" + "encoding/json" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" +) + +// Vote represents a single vote that an authorized signer made to modify the +// list of authorizations. +type Vote struct { + Signer common.Address `json:"signer"` // Authorized signer that cast this vote + Block uint64 `json:"block"` // Block number the vote was cast in (expire old votes) + Address common.Address `json:"address"` // Account being voted on to change its authorization + Authorize bool `json:"authorize"` // Whether to authorize or deauthorize the voted account +} + +// Tally is a simple vote tally to keep the current score of votes. Votes that +// go against the proposal aren't counted since it's equivalent to not voting. +type Tally struct { + Authorize bool `json:"authorize"` // Whether the vote it about authorizing or kicking someone + Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal +} + +// Snapshot is the state of the authorization voting at a given point in time. +type Snapshot struct { + config *params.CliqueConfig // Consensus engine parameters to fine tune behavior + + Number uint64 `json:"number"` // Block number where the snapshot was created + Hash common.Hash `json:"hash"` // Block hash where the snapshot was created + Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment + Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections + Votes []*Vote `json:"votes"` // List of votes cast in chronological order + Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating +} + +// newSnapshot create a new snapshot with the specified startup parameters. This +// method does not initialize the set of recent signers, so only ever use if for +// the genesis block. +func newSnapshot(config *params.CliqueConfig, number uint64, hash common.Hash, signers []common.Address) *Snapshot { + snap := &Snapshot{ + config: config, + Number: number, + Hash: hash, + Signers: make(map[common.Address]struct{}), + Recents: make(map[uint64]common.Address), + Tally: make(map[common.Address]Tally), + } + for _, signer := range signers { + snap.Signers[signer] = struct{}{} + } + return snap +} + +// loadSnapshot loads an existing snapshot from the database. +func loadSnapshot(config *params.CliqueConfig, db ethdb.Database, hash common.Hash) (*Snapshot, error) { + blob, err := db.Get(append([]byte("clique-"), hash[:]...)) + if err != nil { + return nil, err + } + snap := new(Snapshot) + if err := json.Unmarshal(blob, snap); err != nil { + return nil, err + } + snap.config = config + + return snap, nil +} + +// store inserts the snapshot into the database. +func (s *Snapshot) store(db ethdb.Database) error { + blob, err := json.Marshal(s) + if err != nil { + return err + } + return db.Put(append([]byte("clique-"), s.Hash[:]...), blob) +} + +// copy creates a deep copy of the snapshot, though not the individual votes. +func (s *Snapshot) copy() *Snapshot { + cpy := &Snapshot{ + config: s.config, + Number: s.Number, + Hash: s.Hash, + Signers: make(map[common.Address]struct{}), + Recents: make(map[uint64]common.Address), + Votes: make([]*Vote, len(s.Votes)), + Tally: make(map[common.Address]Tally), + } + for signer := range s.Signers { + cpy.Signers[signer] = struct{}{} + } + for block, signer := range s.Recents { + cpy.Recents[block] = signer + } + for address, tally := range s.Tally { + cpy.Tally[address] = tally + } + copy(cpy.Votes, s.Votes) + + return cpy +} + +// cast adds a new vote into the tally. +func (s *Snapshot) cast(address common.Address, authorize bool) bool { + // Ensure the vote is meaningful + _, signer := s.Signers[address] + if (signer && authorize) || (!signer && !authorize) { + return false + } + // Cast the vote into an existing or new tally + if old, ok := s.Tally[address]; ok { + old.Votes++ + s.Tally[address] = old + } else { + s.Tally[address] = Tally{Authorize: authorize, Votes: 1} + } + return true +} + +// uncast removes a previously cast vote from the tally. +func (s *Snapshot) uncast(address common.Address, authorize bool) bool { + // If there's no tally, it's a dangling vote, just drop + tally, ok := s.Tally[address] + if !ok { + return false + } + // Ensure we only revert counted votes + if tally.Authorize != authorize { + return false + } + // Otherwise revert the vote + if tally.Votes > 1 { + tally.Votes-- + s.Tally[address] = tally + } else { + delete(s.Tally, address) + } + return true +} + +// apply creates a new authorization snapshot by applying the given headers to +// the original one. +func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { + // Allow passing in no headers for cleaner code + if len(headers) == 0 { + return s, nil + } + // Sanity check that the headers can be applied + for i := 0; i < len(headers)-1; i++ { + if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { + return nil, errInvalidVotingChain + } + } + if headers[0].Number.Uint64() != s.Number+1 { + return nil, errInvalidVotingChain + } + // Iterate through the headers and create a new snapshot + snap := s.copy() + + for _, header := range headers { + // Remove any votes on checkpoint blocks + number := header.Number.Uint64() + if number%s.config.Epoch == 0 { + snap.Votes = nil + snap.Tally = make(map[common.Address]Tally) + } + // Delete the oldest signer from the recent list to allow it signing again + if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { + delete(snap.Recents, number-limit) + } + // Resolve the authorization key and check against signers + signer, err := ecrecover(header) + if err != nil { + return nil, err + } + if _, ok := snap.Signers[signer]; !ok { + return nil, errUnauthorized + } + for _, recent := range snap.Recents { + if recent == signer { + return nil, errUnauthorized + } + } + snap.Recents[number] = signer + + // Header authorized, discard any previous votes from the signer + for i, vote := range snap.Votes { + if vote.Signer == signer && vote.Address == header.Coinbase { + // Uncast the vote from the cached tally + snap.uncast(vote.Address, vote.Authorize) + + // Uncast the vote from the chronological list + snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) + break // only one vote allowed + } + } + // Tally up the new vote from the signer + var authorize bool + switch { + case bytes.Compare(header.Nonce[:], nonceAuthVote) == 0: + authorize = true + case bytes.Compare(header.Nonce[:], nonceDropVote) == 0: + authorize = false + default: + return nil, errInvalidVote + } + if snap.cast(header.Coinbase, authorize) { + snap.Votes = append(snap.Votes, &Vote{ + Signer: signer, + Block: number, + Address: header.Coinbase, + Authorize: authorize, + }) + } + // If the vote passed, update the list of signers + if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 { + if tally.Authorize { + snap.Signers[header.Coinbase] = struct{}{} + } else { + delete(snap.Signers, header.Coinbase) + + // Signer list shrunk, delete any leftover recent caches + if limit := uint64(len(snap.Signers)/2 + 1); number >= limit { + delete(snap.Recents, number-limit) + } + // Discard any previous votes the deauthorized signer cast + for i := 0; i < len(snap.Votes); i++ { + if snap.Votes[i].Signer == header.Coinbase { + // Uncast the vote from the cached tally + snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize) + + // Uncast the vote from the chronological list + snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) + + i-- + } + } + } + // Discard any previous votes around the just changed account + for i := 0; i < len(snap.Votes); i++ { + if snap.Votes[i].Address == header.Coinbase { + snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...) + i-- + } + } + delete(snap.Tally, header.Coinbase) + } + } + snap.Number += uint64(len(headers)) + snap.Hash = headers[len(headers)-1].Hash() + + return snap, nil +} + +// signers retrieves the list of authorized signers in ascending order. +func (s *Snapshot) signers() []common.Address { + signers := make([]common.Address, 0, len(s.Signers)) + for signer := range s.Signers { + signers = append(signers, signer) + } + for i := 0; i < len(signers); i++ { + for j := i + 1; j < len(signers); j++ { + if bytes.Compare(signers[i][:], signers[j][:]) > 0 { + signers[i], signers[j] = signers[j], signers[i] + } + } + } + return signers +} + +// inturn returns if a signer at a given block height is in-turn or not. +func (s *Snapshot) inturn(number uint64, signer common.Address) bool { + signers, offset := s.signers(), 0 + for offset < len(signers) && signers[offset] != signer { + offset++ + } + return (number % uint64(len(signers))) == uint64(offset) +} diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go new file mode 100644 index 000000000..49a1d7d5b --- /dev/null +++ b/consensus/clique/snapshot_test.go @@ -0,0 +1,405 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package clique + +import ( + "bytes" + "crypto/ecdsa" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" +) + +type testerVote struct { + signer string + voted string + auth bool +} + +// testerAccountPool is a pool to maintain currently active tester accounts, +// mapped from textual names used in the tests below to actual Ethereum private +// keys capable of signing transactions. +type testerAccountPool struct { + accounts map[string]*ecdsa.PrivateKey +} + +func newTesterAccountPool() *testerAccountPool { + return &testerAccountPool{ + accounts: make(map[string]*ecdsa.PrivateKey), + } +} + +func (ap *testerAccountPool) sign(header *types.Header, signer string) { + // Ensure we have a persistent key for the signer + if ap.accounts[signer] == nil { + ap.accounts[signer], _ = crypto.GenerateKey() + } + // Sign the header and embed the signature in extra data + sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer]) + copy(header.Extra[len(header.Extra)-65:], sig) +} + +func (ap *testerAccountPool) address(account string) common.Address { + // Ensure we have a persistent key for the account + if ap.accounts[account] == nil { + ap.accounts[account], _ = crypto.GenerateKey() + } + // Resolve and return the Ethereum address + return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) +} + +// testerChainReader implements consensus.ChainReader to access the genesis +// block. All other methods and requests will panic. +type testerChainReader struct { + db ethdb.Database +} + +func (r *testerChainReader) Config() *params.ChainConfig { panic("not supported") } +func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") } +func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") } +func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") } +func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") } +func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header { + if number == 0 { + return core.GetHeader(r.db, core.GetCanonicalHash(r.db, 0), 0) + } + panic("not supported") +} + +// Tests that voting is evaluated correctly for various simple and complex scenarios. +func TestVoting(t *testing.T) { + // Define the various voting scenarios to test + tests := []struct { + epoch uint64 + signers []string + votes []testerVote + results []string + }{ + { + // Single signer, no votes cast + signers: []string{"A"}, + votes: []testerVote{{signer: "A"}}, + results: []string{"A"}, + }, { + // Single signer, voting to add two others (only accept first, second needs 2 votes) + signers: []string{"A"}, + votes: []testerVote{ + {signer: "A", voted: "B", auth: true}, + {signer: "B"}, + {signer: "A", voted: "C", auth: true}, + }, + results: []string{"A", "B"}, + }, { + // Two signers, voting to add three others (only accept first two, third needs 3 votes already) + signers: []string{"A", "B"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: true}, + {signer: "B", voted: "C", auth: true}, + {signer: "A", voted: "D", auth: true}, + {signer: "B", voted: "D", auth: true}, + {signer: "C"}, + {signer: "A", voted: "E", auth: true}, + {signer: "B", voted: "E", auth: true}, + }, + results: []string{"A", "B", "C", "D"}, + }, { + // Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this) + signers: []string{"A"}, + votes: []testerVote{ + {signer: "A", voted: "A", auth: false}, + }, + results: []string{}, + }, { + // Two signers, actually needing mutual consent to drop either of them (not fulfilled) + signers: []string{"A", "B"}, + votes: []testerVote{ + {signer: "A", voted: "B", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Two signers, actually needing mutual consent to drop either of them (fulfilled) + signers: []string{"A", "B"}, + votes: []testerVote{ + {signer: "A", voted: "B", auth: false}, + {signer: "B", voted: "B", auth: false}, + }, + results: []string{"A"}, + }, { + // Three signers, two of them deciding to drop the third + signers: []string{"A", "B", "C"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: false}, + {signer: "B", voted: "C", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Four signers, consensus of two not being enough to drop anyone + signers: []string{"A", "B", "C", "D"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: false}, + {signer: "B", voted: "C", auth: false}, + }, + results: []string{"A", "B", "C", "D"}, + }, { + // Four signers, consensus of three already being enough to drop someone + signers: []string{"A", "B", "C", "D"}, + votes: []testerVote{ + {signer: "A", voted: "D", auth: false}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + }, + results: []string{"A", "B", "C"}, + }, { + // Authorizations are counted once per signer per target + signers: []string{"A", "B"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: true}, + {signer: "B"}, + {signer: "A", voted: "C", auth: true}, + {signer: "B"}, + {signer: "A", voted: "C", auth: true}, + }, + results: []string{"A", "B"}, + }, { + // Authorizing multiple accounts concurrently is permitted + signers: []string{"A", "B"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: true}, + {signer: "B"}, + {signer: "A", voted: "D", auth: true}, + {signer: "B"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: true}, + {signer: "A"}, + {signer: "B", voted: "C", auth: true}, + }, + results: []string{"A", "B", "C", "D"}, + }, { + // Deauthorizations are counted once per signer per target + signers: []string{"A", "B"}, + votes: []testerVote{ + {signer: "A", voted: "B", auth: false}, + {signer: "B"}, + {signer: "A", voted: "B", auth: false}, + {signer: "B"}, + {signer: "A", voted: "B", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Deauthorizing multiple accounts concurrently is permitted + signers: []string{"A", "B", "C", "D"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A", voted: "D", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + {signer: "A"}, + {signer: "B", voted: "C", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Votes from deauthorized signers are discarded immediately (deauth votes) + signers: []string{"A", "B", "C"}, + votes: []testerVote{ + {signer: "C", voted: "B", auth: false}, + {signer: "A", voted: "C", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "A", voted: "B", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Votes from deauthorized signers are discarded immediately (auth votes) + signers: []string{"A", "B", "C"}, + votes: []testerVote{ + {signer: "C", voted: "B", auth: false}, + {signer: "A", voted: "C", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "A", voted: "B", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Cascading changes are not allowed, only the the account being voted on may change + signers: []string{"A", "B", "C", "D"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A", voted: "D", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "C"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + }, + results: []string{"A", "B", "C"}, + }, { + // Changes reaching consensus out of bounds (via a deauth) execute on touch + signers: []string{"A", "B", "C", "D"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A", voted: "D", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "C"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + {signer: "A"}, + {signer: "C", voted: "C", auth: true}, + }, + results: []string{"A", "B"}, + }, { + // Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch + signers: []string{"A", "B", "C", "D"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A", voted: "D", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "C"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + {signer: "A"}, + {signer: "B", voted: "C", auth: true}, + }, + results: []string{"A", "B", "C"}, + }, { + // Ensure that pending votes don't survive authorization status changes. This + // corner case can only appear if a signer is quickly added, remove and then + // readded (or the inverse), while one of the original voters dropped. If a + // past vote is left cached in the system somewhere, this will interfere with + // the final signer outcome. + signers: []string{"A", "B", "C", "D", "E"}, + votes: []testerVote{ + {signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed + {signer: "B", voted: "F", auth: true}, + {signer: "C", voted: "F", auth: true}, + {signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged") + {signer: "E", voted: "F", auth: false}, + {signer: "B", voted: "F", auth: false}, + {signer: "C", voted: "F", auth: false}, + {signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed + {signer: "E", voted: "F", auth: true}, + {signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed + {signer: "C", voted: "A", auth: false}, + {signer: "D", voted: "A", auth: false}, + {signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed + }, + results: []string{"B", "C", "D", "E", "F"}, + }, { + // Epoch transitions reset all votes to allow chain checkpointing + epoch: 3, + signers: []string{"A", "B"}, + votes: []testerVote{ + {signer: "A", voted: "C", auth: true}, + {signer: "B"}, + {signer: "A"}, // Checkpoint block, (don't vote here, it's validated outside of snapshots) + {signer: "B", voted: "C", auth: true}, + }, + results: []string{"A", "B"}, + }, + } + // Run through the scenarios and test them + for i, tt := range tests { + // Create the account pool and generate the initial set of signers + accounts := newTesterAccountPool() + + signers := make([]common.Address, len(tt.signers)) + for j, signer := range tt.signers { + signers[j] = accounts.address(signer) + } + for j := 0; j < len(signers); j++ { + for k := j + 1; k < len(signers); k++ { + if bytes.Compare(signers[j][:], signers[k][:]) > 0 { + signers[j], signers[k] = signers[k], signers[j] + } + } + } + // Create the genesis block with the initial set of signers + genesis := &core.Genesis{ + ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal), + } + for j, signer := range signers { + copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) + } + // Create a pristine blockchain with the genesis injected + db, _ := ethdb.NewMemDatabase() + genesis.Commit(db) + + // Assemble a chain of headers from the cast votes + headers := make([]*types.Header, len(tt.votes)) + for j, vote := range tt.votes { + headers[j] = &types.Header{ + Number: big.NewInt(int64(j) + 1), + Time: big.NewInt(int64(j) * int64(blockPeriod)), + Coinbase: accounts.address(vote.voted), + Extra: make([]byte, extraVanity+extraSeal), + } + if j > 0 { + headers[j].ParentHash = headers[j-1].Hash() + } + if vote.auth { + copy(headers[j].Nonce[:], nonceAuthVote) + } + accounts.sign(headers[j], vote.signer) + } + // Pass all the headers through clique and ensure tallying succeeds + head := headers[len(headers)-1] + + snap, err := New(¶ms.CliqueConfig{Epoch: tt.epoch}, db).snapshot(&testerChainReader{db: db}, head.Number.Uint64(), head.Hash(), headers) + if err != nil { + t.Errorf("test %d: failed to create voting snapshot: %v", i, err) + continue + } + // Verify the final list of signers against the expected ones + signers = make([]common.Address, len(tt.results)) + for j, signer := range tt.results { + signers[j] = accounts.address(signer) + } + for j := 0; j < len(signers); j++ { + for k := j + 1; k < len(signers); k++ { + if bytes.Compare(signers[j][:], signers[k][:]) > 0 { + signers[j], signers[k] = signers[k], signers[j] + } + } + } + result := snap.signers() + if len(result) != len(signers) { + t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers) + continue + } + for j := 0; j < len(result); j++ { + if !bytes.Equal(result[j][:], signers[j][:]) { + t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j]) + } + } + } +} diff --git a/consensus/consensus.go b/consensus/consensus.go new file mode 100644 index 000000000..8cbd32c88 --- /dev/null +++ b/consensus/consensus.go @@ -0,0 +1,102 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package consensus implements different Ethereum consensus engines. +package consensus + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" +) + +// ChainReader defines a small collection of methods needed to access the local +// blockchain during header and/or uncle verification. +type ChainReader interface { + // Config retrieves the blockchain's chain configuration. + Config() *params.ChainConfig + + // CurrentHeader retrieves the current header from the local chain. + CurrentHeader() *types.Header + + // GetHeader retrieves a block header from the database by hash and number. + GetHeader(hash common.Hash, number uint64) *types.Header + + // GetHeaderByNumber retrieves a block header from the database by number. + GetHeaderByNumber(number uint64) *types.Header + + // GetHeaderByHash retrieves a block header from the database by its hash. + GetHeaderByHash(hash common.Hash) *types.Header + + // GetBlock retrieves a block from the database by hash and number. + GetBlock(hash common.Hash, number uint64) *types.Block +} + +// Engine is an algorithm agnostic consensus engine. +type Engine interface { + // Author retrieves the Ethereum address of the account that minted the given + // block, which may be different from the header's coinbase if a consensus + // engine is based on signatures. + Author(header *types.Header) (common.Address, error) + + // VerifyHeader checks whether a header conforms to the consensus rules of a + // given engine. Verifying the seal may be done optionally here, or explicitly + // via the VerifySeal method. + VerifyHeader(chain ChainReader, header *types.Header, seal bool) error + + // 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 (the order is that of + // the input slice). + VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) + + // VerifyUncles verifies that the given block's uncles conform to the consensus + // rules of a given engine. + VerifyUncles(chain ChainReader, block *types.Block) error + + // VerifySeal checks whether the crypto seal on a header is valid according to + // the consensus rules of the given engine. + VerifySeal(chain ChainReader, header *types.Header) error + + // Prepare initializes the consensus fields of a block header according to the + // rules of a particular engine. The changes are executed inline. + Prepare(chain ChainReader, header *types.Header) error + + // Finalize runs any post-transaction state modifications (e.g. block rewards) + // and assembles the final block. + // + // Note, the block header and state database might be updated to reflect any + // consensus rules that happen at finalization (e.g. block rewards). + Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, + uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) + + // Seal generates a new block for the given input block with the local miner's + // seal place on top. + Seal(chain ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) + + // APIs returns the RPC APIs this consensus engine provides. + APIs(chain ChainReader) []rpc.API +} + +// PoW is a consensus engine based on proof-of-work. +type PoW interface { + Engine + + // Hashrate returns the current mining hashrate of a PoW consensus engine. + Hashrate() float64 +} diff --git a/consensus/errors.go b/consensus/errors.go new file mode 100644 index 000000000..3b136dbdd --- /dev/null +++ b/consensus/errors.go @@ -0,0 +1,33 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package consensus + +import "errors" + +var ( + // ErrUnknownAncestor is returned when validating a block requires an ancestor + // that is unknown. + ErrUnknownAncestor = errors.New("unknown ancestor") + + // ErrFutureBlock is returned when a block's timestamp is in the future according + // to the current node. + ErrFutureBlock = errors.New("block in the future") + + // ErrInvalidNumber is returned if a block's number doesn't equal it's parent's + // plus one. + ErrInvalidNumber = errors.New("invalid block number") +) diff --git a/pow/ethash_algo.go b/consensus/ethash/algorithm.go index 3737cc5d7..7e8fbfc37 100644 --- a/pow/ethash_algo.go +++ b/consensus/ethash/algorithm.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -package pow +package ethash import ( "encoding/binary" @@ -349,12 +349,12 @@ func hashimotoLight(size uint64, cache []uint32, hash []byte, nonce uint64) ([]b // hashimotoFull aggregates data from the full dataset (using the full in-memory // dataset) in order to produce our final value for a particular header hash and // nonce. -func hashimotoFull(size uint64, dataset []uint32, hash []byte, nonce uint64) ([]byte, []byte) { +func hashimotoFull(dataset []uint32, hash []byte, nonce uint64) ([]byte, []byte) { lookup := func(index uint32) []uint32 { offset := index * hashWords return dataset[offset : offset+hashWords] } - return hashimoto(hash, nonce, size, lookup) + return hashimoto(hash, nonce, uint64(len(dataset))*4, lookup) } // datasetSizes is a lookup table for the ethash dataset size for the first 2048 diff --git a/pow/ethash_algo_go1.7.go b/consensus/ethash/algorithm_go1.7.go index ce05b3bb0..c34d041c3 100644 --- a/pow/ethash_algo_go1.7.go +++ b/consensus/ethash/algorithm_go1.7.go @@ -16,7 +16,7 @@ // +build !go1.8 -package pow +package ethash // cacheSize calculates and returns the size of the ethash verification cache that // belongs to a certain block number. The cache size grows linearly, however, we diff --git a/pow/ethash_algo_go1.8.go b/consensus/ethash/algorithm_go1.8.go index cac96cd5e..62bf4dec1 100644 --- a/pow/ethash_algo_go1.8.go +++ b/consensus/ethash/algorithm_go1.8.go @@ -16,7 +16,7 @@ // +build go1.8 -package pow +package ethash import "math/big" diff --git a/pow/ethash_algo_go1.8_test.go b/consensus/ethash/algorithm_go1.8_test.go index 57e0b0b7a..fdc302318 100644 --- a/pow/ethash_algo_go1.8_test.go +++ b/consensus/ethash/algorithm_go1.8_test.go @@ -16,7 +16,7 @@ // +build go1.8 -package pow +package ethash import "testing" diff --git a/pow/ethash_algo_test.go b/consensus/ethash/algorithm_test.go index c881874ff..7e4307a74 100644 --- a/pow/ethash_algo_test.go +++ b/consensus/ethash/algorithm_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -package pow +package ethash import ( "bytes" @@ -660,7 +660,7 @@ func TestHashimoto(t *testing.T) { if !bytes.Equal(result, wantResult) { t.Errorf("light hashimoto result mismatch: have %x, want %x", result, wantResult) } - digest, result = hashimotoFull(32*1024, dataset, hash, nonce) + digest, result = hashimotoFull(dataset, hash, nonce) if !bytes.Equal(digest, wantDigest) { t.Errorf("full hashimoto digest mismatch: have %x, want %x", digest, wantDigest) } @@ -704,8 +704,8 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) { go func(idx int) { defer pend.Done() - ethash := NewFullEthash(cachedir, 0, 1, "", 0, 0) - if err := ethash.Verify(block); err != nil { + ethash := New(cachedir, 0, 1, "", 0, 0) + if err := ethash.VerifySeal(nil, block.Header()); err != nil { t.Errorf("proc %d: block verification failed: %v", idx, err) } }(i) @@ -758,6 +758,6 @@ func BenchmarkHashimotoFullSmall(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - hashimotoFull(32*65536, dataset, hash, 0) + hashimotoFull(dataset, hash, 0) } } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go new file mode 100644 index 000000000..4f1ab8702 --- /dev/null +++ b/consensus/ethash/consensus.go @@ -0,0 +1,470 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package ethash + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "runtime" + "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/misc" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + set "gopkg.in/fatih/set.v0" +) + +// Ethash proof-of-work protocol constants. +var ( + blockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block + maxUncles = 2 // Maximum number of uncles allowed in a single block +) + +// 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 ( + errLargeBlockTime = errors.New("timestamp too big") + errZeroBlockTime = errors.New("timestamp equals parent's") + errTooManyUncles = errors.New("too many uncles") + errDuplicateUncle = errors.New("duplicate uncle") + errUncleIsAncestor = errors.New("uncle is ancestor") + errDanglingUncle = errors.New("uncle's parent is not ancestor") + errNonceOutOfRange = errors.New("nonce out of range") + errInvalidDifficulty = errors.New("non-positive difficulty") + errInvalidMixDigest = errors.New("invalid mix digest") + errInvalidPoW = errors.New("invalid proof-of-work") +) + +// Author implements consensus.Engine, returning the header's coinbase as the +// proof-of-work verified author of the block. +func (ethash *Ethash) Author(header *types.Header) (common.Address, error) { + return header.Coinbase, nil +} + +// VerifyHeader checks whether a header conforms to the consensus rules of the +// stock Ethereum ethash engine. +func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { + // If we're running a full engine faking, accept any input as valid + if ethash.fakeFull { + return nil + } + // Short circuit if the header is known, or it's 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 ethash.verifyHeader(chain, header, parent, false, seal) +} + +// 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 (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { + // If we're running a full engine faking, accept any input as valid + if ethash.fakeFull || len(headers) == 0 { + abort, results := make(chan struct{}), make(chan error, len(headers)) + for i := 0; i < len(headers); i++ { + results <- nil + } + return abort, results + } + + // Spawn as many workers as allowed threads + workers := runtime.GOMAXPROCS(0) + if len(headers) < workers { + workers = len(headers) + } + + // Create a task channel and spawn the verifiers + var ( + inputs = make(chan int) + done = make(chan int, workers) + errors = make([]error, len(headers)) + abort = make(chan struct{}) + ) + for i := 0; i < workers; i++ { + go func() { + for index := range inputs { + errors[index] = ethash.verifyHeaderWorker(chain, headers, seals, index) + done <- index + } + }() + } + + errorsOut := make(chan error, len(headers)) + go func() { + defer close(inputs) + var ( + in, out = 0, 0 + checked = make([]bool, len(headers)) + inputs = inputs + ) + for { + select { + case inputs <- in: + if in++; in == len(headers) { + // Reached end of headers. Stop sending to workers. + inputs = nil + } + case index := <-done: + for checked[index] = true; checked[out]; out++ { + errorsOut <- errors[out] + if out == len(headers)-1 { + return + } + } + case <-abort: + return + } + } + }() + return abort, errorsOut +} + +func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainReader, headers []*types.Header, seals []bool, index int) error { + var parent *types.Header + if index == 0 { + parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) + } else if headers[index-1].Hash() == headers[index].ParentHash { + parent = headers[index-1] + } + if parent == nil { + return consensus.ErrUnknownAncestor + } + if chain.GetHeader(headers[index].Hash(), headers[index].Number.Uint64()) != nil { + return nil // known block + } + return ethash.verifyHeader(chain, headers[index], parent, false, seals[index]) +} + +// VerifyUncles verifies that the given block's uncles conform to the consensus +// rules of the stock Ethereum ethash engine. +func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { + // If we're running a full engine faking, accept any input as valid + if ethash.fakeFull { + return nil + } + // Verify that there are at most 2 uncles included in this block + if len(block.Uncles()) > maxUncles { + return errTooManyUncles + } + // Gather the set of past uncles and ancestors + uncles, ancestors := set.New(), make(map[common.Hash]*types.Header) + + number, parent := block.NumberU64()-1, block.ParentHash() + for i := 0; i < 7; i++ { + ancestor := chain.GetBlock(parent, number) + if ancestor == nil { + break + } + ancestors[ancestor.Hash()] = ancestor.Header() + for _, uncle := range ancestor.Uncles() { + uncles.Add(uncle.Hash()) + } + parent, number = ancestor.ParentHash(), number-1 + } + ancestors[block.Hash()] = block.Header() + uncles.Add(block.Hash()) + + // Verify each of the uncles that it's recent, but not an ancestor + for _, uncle := range block.Uncles() { + // Make sure every uncle is rewarded only once + hash := uncle.Hash() + if uncles.Has(hash) { + return errDuplicateUncle + } + uncles.Add(hash) + + // Make sure the uncle has a valid ancestry + if ancestors[hash] != nil { + return errUncleIsAncestor + } + if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == block.ParentHash() { + return errDanglingUncle + } + if err := ethash.verifyHeader(chain, uncle, ancestors[uncle.ParentHash], true, true); err != nil { + return err + } + } + return nil +} + +// verifyHeader checks whether a header conforms to the consensus rules of the +// stock Ethereum ethash engine. +// +// See YP section 4.3.4. "Block Header Validity" +func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error { + // Ensure that the header's extra-data section is of a reasonable size + if uint64(len(header.Extra)) > params.MaximumExtraDataSize { + return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize) + } + // Verify the header's timestamp + if uncle { + if header.Time.Cmp(math.MaxBig256) > 0 { + return errLargeBlockTime + } + } else { + if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { + return consensus.ErrFutureBlock + } + } + if header.Time.Cmp(parent.Time) <= 0 { + return errZeroBlockTime + } + // Verify the block's difficulty based in it's timestamp and parent's difficulty + expected := CalcDifficulty(chain.Config(), header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) + if expected.Cmp(header.Difficulty) != 0 { + return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected) + } + // Verify that the gas limit remains within allowed bounds + diff := new(big.Int).Set(parent.GasLimit) + diff = diff.Sub(diff, header.GasLimit) + diff.Abs(diff) + + limit := new(big.Int).Set(parent.GasLimit) + limit = limit.Div(limit, params.GasLimitBoundDivisor) + + if diff.Cmp(limit) >= 0 || header.GasLimit.Cmp(params.MinGasLimit) < 0 { + return fmt.Errorf("invalid gas limit: have %v, want %v += %v", 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 engine specific seal securing the block + if seal { + if err := ethash.VerifySeal(chain, header); err != nil { + return err + } + } + // If all checks passed, validate any special fields for hard forks + if err := misc.VerifyDAOHeaderExtraData(chain.Config(), header); err != nil { + return err + } + if err := misc.VerifyForkHashes(chain.Config(), header, uncle); err != nil { + return err + } + return nil +} + +// 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. +// +// TODO (karalabe): Move the chain maker into this package and make this private! +func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { + if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { + return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff) + } + return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff) +} + +// Some weird constants to avoid constant memory allocs for them. +var ( + expDiffPeriod = big.NewInt(100000) + big10 = big.NewInt(10) + bigMinus99 = big.NewInt(-99) +) + +// calcDifficultyHomestead 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. The calculation uses the Homestead rules. +func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki + // algorithm: + // diff = (parent_diff + + // (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) + // ) + 2^(periodCount - 2) + + bigTime := new(big.Int).SetUint64(time) + bigParentTime := new(big.Int).SetUint64(parentTime) + + // holds intermediate values to make the algo easier to read & audit + x := new(big.Int) + y := new(big.Int) + + // 1 - (block_timestamp -parent_timestamp) // 10 + x.Sub(bigTime, bigParentTime) + x.Div(x, big10) + x.Sub(common.Big1, x) + + // max(1 - (block_timestamp - parent_timestamp) // 10, -99))) + if x.Cmp(bigMinus99) < 0 { + x.Set(bigMinus99) + } + // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) + y.Div(parentDiff, params.DifficultyBoundDivisor) + x.Mul(y, x) + x.Add(parentDiff, x) + + // minimum difficulty can ever be (before exponential factor) + if x.Cmp(params.MinimumDifficulty) < 0 { + x.Set(params.MinimumDifficulty) + } + // for the exponential factor + periodCount := new(big.Int).Add(parentNumber, common.Big1) + periodCount.Div(periodCount, expDiffPeriod) + + // the exponential factor, commonly referred to as "the bomb" + // diff = diff + 2^(periodCount - 2) + if periodCount.Cmp(common.Big1) > 0 { + y.Sub(periodCount, common.Big2) + y.Exp(common.Big2, y, nil) + x.Add(x, y) + } + return x +} + +// calcDifficultyFrontier 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. The calculation uses the Frontier rules. +func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { + diff := new(big.Int) + adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor) + bigTime := new(big.Int) + bigParentTime := new(big.Int) + + bigTime.SetUint64(time) + bigParentTime.SetUint64(parentTime) + + if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 { + diff.Add(parentDiff, adjust) + } else { + diff.Sub(parentDiff, adjust) + } + if diff.Cmp(params.MinimumDifficulty) < 0 { + diff.Set(params.MinimumDifficulty) + } + + periodCount := new(big.Int).Add(parentNumber, common.Big1) + periodCount.Div(periodCount, expDiffPeriod) + if periodCount.Cmp(common.Big1) > 0 { + // diff = diff + 2^(periodCount - 2) + expDiff := periodCount.Sub(periodCount, common.Big2) + expDiff.Exp(common.Big2, expDiff, nil) + diff.Add(diff, expDiff) + diff = math.BigMax(diff, params.MinimumDifficulty) + } + return diff +} + +// VerifySeal implements consensus.Engine, checking whether the given block satisfies +// the PoW difficulty requirements. +func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Header) error { + // If we're running a fake PoW, accept any seal as valid + if ethash.fakeMode { + time.Sleep(ethash.fakeDelay) + if ethash.fakeFail == header.Number.Uint64() { + return errInvalidPoW + } + return nil + } + // If we're running a shared PoW, delegate verification to it + if ethash.shared != nil { + return ethash.shared.VerifySeal(chain, header) + } + // Sanity check that the block number is below the lookup table size (60M blocks) + number := header.Number.Uint64() + if number/epochLength >= uint64(len(cacheSizes)) { + // Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check) + return errNonceOutOfRange + } + // Ensure that we have a valid difficulty for the block + if header.Difficulty.Sign() <= 0 { + return errInvalidDifficulty + } + // Recompute the digest and PoW value and verify against the header + cache := ethash.cache(number) + + size := datasetSize(number) + if ethash.tester { + size = 32 * 1024 + } + digest, result := hashimotoLight(size, cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64()) + if !bytes.Equal(header.MixDigest[:], digest) { + return errInvalidMixDigest + } + target := new(big.Int).Div(maxUint256, header.Difficulty) + if new(big.Int).SetBytes(result).Cmp(target) > 0 { + return errInvalidPoW + } + return nil +} + +// Prepare implements consensus.Engine, initializing the difficulty field of a +// header to conform to the ethash protocol. The changes are done inline. +func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) error { + parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } + header.Difficulty = CalcDifficulty(chain.Config(), header.Time.Uint64(), + parent.Time.Uint64(), parent.Number, parent.Difficulty) + + return nil +} + +// Finalize implements consensus.Engine, accumulating the block and uncle rewards, +// setting the final state and assembling the block. +func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { + // Accumulate any block and uncle rewards and commit the final state root + AccumulateRewards(state, header, uncles) + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + + // Header seems complete, assemble into a block and return + return types.NewBlock(header, txs, uncles, receipts), nil +} + +// Some weird constants to avoid constant memory allocs for them. +var ( + big8 = big.NewInt(8) + big32 = big.NewInt(32) +) + +// AccumulateRewards credits the coinbase of the given block with the mining +// reward. The total reward consists of the static block reward and rewards for +// included uncles. The coinbase of each uncle block is also rewarded. +// +// TODO (karalabe): Move the chain maker into this package and make this private! +func AccumulateRewards(state *state.StateDB, header *types.Header, uncles []*types.Header) { + reward := new(big.Int).Set(blockReward) + r := new(big.Int) + for _, uncle := range uncles { + r.Add(uncle.Number, big8) + r.Sub(r, header.Number) + r.Mul(r, blockReward) + r.Div(r, big8) + state.AddBalance(uncle.Coinbase, r) + + r.Div(blockReward, big32) + reward.Add(reward, r) + } + state.AddBalance(header.Coinbase, reward) +} diff --git a/consensus/ethash/consensus_test.go b/consensus/ethash/consensus_test.go new file mode 100644 index 000000000..683c10be4 --- /dev/null +++ b/consensus/ethash/consensus_test.go @@ -0,0 +1,79 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package ethash + +import ( + "encoding/json" + "math/big" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/params" +) + +type diffTest struct { + ParentTimestamp uint64 + ParentDifficulty *big.Int + CurrentTimestamp uint64 + CurrentBlocknumber *big.Int + CurrentDifficulty *big.Int +} + +func (d *diffTest) UnmarshalJSON(b []byte) (err error) { + var ext struct { + ParentTimestamp string + ParentDifficulty string + CurrentTimestamp string + CurrentBlocknumber string + CurrentDifficulty string + } + if err := json.Unmarshal(b, &ext); err != nil { + return err + } + + d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp) + d.ParentDifficulty = math.MustParseBig256(ext.ParentDifficulty) + d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp) + d.CurrentBlocknumber = math.MustParseBig256(ext.CurrentBlocknumber) + d.CurrentDifficulty = math.MustParseBig256(ext.CurrentDifficulty) + + return nil +} + +func TestCalcDifficulty(t *testing.T) { + file, err := os.Open("../../tests/files/BasicTests/difficulty.json") + if err != nil { + t.Fatal(err) + } + defer file.Close() + + tests := make(map[string]diffTest) + err = json.NewDecoder(file).Decode(&tests) + if err != nil { + t.Fatal(err) + } + + config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)} + for name, test := range tests { + number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1)) + diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty) + if diff.Cmp(test.CurrentDifficulty) != 0 { + t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff) + } + } +} diff --git a/pow/ethash.go b/consensus/ethash/ethash.go index 1e577a587..d284e7b00 100644 --- a/pow/ethash.go +++ b/consensus/ethash/ethash.go @@ -14,10 +14,10 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -package pow +// Package ethash implements the ethash proof-of-work consensus engine. +package ethash import ( - "bytes" "errors" "fmt" "math" @@ -32,24 +32,20 @@ import ( "unsafe" mmap "github.com/edsrzf/mmap-go" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" metrics "github.com/rcrowley/go-metrics" ) -var ( - ErrInvalidDumpMagic = errors.New("invalid dump magic") - ErrNonceOutOfRange = errors.New("nonce out of range") - ErrInvalidDifficulty = errors.New("non-positive difficulty") - ErrInvalidMixDigest = errors.New("invalid mix digest") - ErrInvalidPoW = errors.New("pow difficulty invalid") -) +var ErrInvalidDumpMagic = errors.New("invalid dump magic") var ( // maxUint256 is a big integer representing 2^256-1 maxUint256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) // sharedEthash is a full instance that can be shared between multiple users. - sharedEthash = NewFullEthash("", 3, 0, "", 1, 0) + sharedEthash = New("", 3, 0, "", 1, 0) // algorithmRevision is the data structure version used for file naming. algorithmRevision = 23 @@ -321,7 +317,8 @@ func MakeDataset(block uint64, dir string) { d.release() } -// Ethash is a PoW data struture implementing the ethash algorithm. +// Ethash is a consensus engine based on proot-of-work implementing the ethash +// algorithm. type Ethash struct { cachedir string // Data directory to store the verification caches cachesinmem int // Number of caches to keep in memory @@ -334,15 +331,26 @@ type Ethash struct { fcache *cache // Pre-generated cache for the estimated future epoch datasets map[uint64]*dataset // In memory datasets to avoid regenerating too often fdataset *dataset // Pre-generated dataset for the estimated future epoch - lock sync.Mutex // Ensures thread safety for the in-memory caches + // Mining related fields + rand *rand.Rand // Properly seeded random source for nonces + threads int // Number of threads to mine on if mining + update chan struct{} // Notification channel to update mining parameters hashrate metrics.Meter // Meter tracking the average hashrate - tester bool // Flag whether to use a smaller test dataset + // The fields below are hooks for testing + tester bool // Flag whether to use a smaller test dataset + shared *Ethash // Shared PoW verifier to avoid cache regeneration + fakeMode bool // Flag whether to disable PoW checking + fakeFull bool // Flag whether to disable all consensus rules + fakeFail uint64 // Block number which fails PoW check even in fake mode + fakeDelay time.Duration // Time delay to sleep for before returning from verify + + lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields } -// NewFullEthash creates a full sized ethash PoW scheme. -func NewFullEthash(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsinmem, dagsondisk int) PoW { +// New creates a full sized ethash PoW scheme. +func New(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsinmem, dagsondisk int) *Ethash { if cachesinmem <= 0 { log.Warn("One ethash cache must alwast be in memory", "requested", cachesinmem) cachesinmem = 1 @@ -362,58 +370,55 @@ func NewFullEthash(cachedir string, cachesinmem, cachesondisk int, dagdir string dagsondisk: dagsondisk, caches: make(map[uint64]*cache), datasets: make(map[uint64]*dataset), + update: make(chan struct{}), hashrate: metrics.NewMeter(), } } -// NewTestEthash creates a small sized ethash PoW scheme useful only for testing +// NewTester creates a small sized ethash PoW scheme useful only for testing // purposes. -func NewTestEthash() PoW { +func NewTester() *Ethash { return &Ethash{ cachesinmem: 1, caches: make(map[uint64]*cache), datasets: make(map[uint64]*dataset), tester: true, + update: make(chan struct{}), hashrate: metrics.NewMeter(), } } -// NewSharedEthash creates a full sized ethash PoW shared between all requesters -// running in the same process. -func NewSharedEthash() PoW { - return sharedEthash +// NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts +// all blocks' seal as valid, though they still have to conform to the Ethereum +// consensus rules. +func NewFaker() *Ethash { + return &Ethash{fakeMode: true} } -// Verify implements PoW, checking whether the given block satisfies the PoW -// difficulty requirements. -func (ethash *Ethash) Verify(block Block) error { - // Sanity check that the block number is below the lookup table size (60M blocks) - number := block.NumberU64() - if number/epochLength >= uint64(len(cacheSizes)) { - // Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check) - return ErrNonceOutOfRange - } - // Ensure that we have a valid difficulty for the block - difficulty := block.Difficulty() - if difficulty.Sign() <= 0 { - return ErrInvalidDifficulty - } - // Recompute the digest and PoW value and verify against the block - cache := ethash.cache(number) +// NewFakeFailer creates a ethash consensus engine with a fake PoW scheme that +// accepts all blocks as valid apart from the single one specified, though they +// still have to conform to the Ethereum consensus rules. +func NewFakeFailer(fail uint64) *Ethash { + return &Ethash{fakeMode: true, fakeFail: fail} +} - size := datasetSize(number) - if ethash.tester { - size = 32 * 1024 - } - digest, result := hashimotoLight(size, cache, block.HashNoNonce().Bytes(), block.Nonce()) - if !bytes.Equal(block.MixDigest().Bytes(), digest) { - return ErrInvalidMixDigest - } - target := new(big.Int).Div(maxUint256, difficulty) - if new(big.Int).SetBytes(result).Cmp(target) > 0 { - return ErrInvalidPoW - } - return nil +// NewFakeDelayer creates a ethash consensus engine with a fake PoW scheme that +// accepts all blocks as valid, but delays verifications by some time, though +// they still have to conform to the Ethereum consensus rules. +func NewFakeDelayer(delay time.Duration) *Ethash { + return &Ethash{fakeMode: true, fakeDelay: delay} +} + +// NewFullFaker creates a ethash consensus engine with a full fake scheme that +// accepts all blocks as valid, without checking any consensus rules whatsoever. +func NewFullFaker() *Ethash { + return &Ethash{fakeMode: true, fakeFull: true} +} + +// NewShared creates a full sized ethash PoW shared between all requesters running +// in the same process. +func NewShared() *Ethash { + return &Ethash{shared: sharedEthash} } // cache tries to retrieve a verification cache for the specified block number @@ -428,7 +433,7 @@ func (ethash *Ethash) cache(block uint64) []uint32 { current, future := ethash.caches[epoch], (*cache)(nil) if current == nil { // No in-memory cache, evict the oldest if the cache limit was reached - for len(ethash.caches) >= ethash.cachesinmem { + for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.cachesinmem { var evict *cache for _, cache := range ethash.caches { if evict == nil || evict.used.After(cache.used) { @@ -477,49 +482,6 @@ func (ethash *Ethash) cache(block uint64) []uint32 { return current.cache } -// Search implements PoW, attempting to find a nonce that satisfies the block's -// difficulty requirements. -func (ethash *Ethash) Search(block Block, stop <-chan struct{}) (uint64, []byte) { - // Extract some data from the block - var ( - hash = block.HashNoNonce().Bytes() - diff = block.Difficulty() - target = new(big.Int).Div(maxUint256, diff) - ) - // Retrieve the mining dataset - dataset, size := ethash.dataset(block.NumberU64()), datasetSize(block.NumberU64()) - - // Start generating random nonces until we abort or find a good one - var ( - attempts int64 - - rand = rand.New(rand.NewSource(time.Now().UnixNano())) - nonce = uint64(rand.Int63()) - ) - for { - select { - case <-stop: - // Mining terminated, update stats and abort - ethash.hashrate.Mark(attempts) - return 0, nil - - default: - // We don't have to update hash rate on every nonce, so update after after 2^X nonces - attempts++ - if (attempts % (1 << 15)) == 0 { - ethash.hashrate.Mark(attempts) - attempts = 0 - } - // Compute the PoW value of this nonce - digest, result := hashimotoFull(size, dataset, hash, nonce) - if new(big.Int).SetBytes(result).Cmp(target) <= 0 { - return nonce, digest - } - nonce++ - } - } -} - // dataset tries to retrieve a mining dataset for the specified block number // by first checking against a list of in-memory datasets, then against DAGs // stored on disk, and finally generating one if none can be found. @@ -532,7 +494,7 @@ func (ethash *Ethash) dataset(block uint64) []uint32 { current, future := ethash.datasets[epoch], (*dataset)(nil) if current == nil { // No in-memory dataset, evict the oldest if the dataset limit was reached - for len(ethash.datasets) >= ethash.dagsinmem { + for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.dagsinmem { var evict *dataset for _, dataset := range ethash.datasets { if evict == nil || evict.used.After(dataset.used) { @@ -582,14 +544,51 @@ func (ethash *Ethash) dataset(block uint64) []uint32 { return current.dataset } +// Threads returns the number of mining threads currently enabled. This doesn't +// necessarily mean that mining is running! +func (ethash *Ethash) Threads() int { + ethash.lock.Lock() + defer ethash.lock.Unlock() + + return ethash.threads +} + +// SetThreads updates the number of mining threads currently enabled. Calling +// this method does not start mining, only sets the thread count. If zero is +// specified, the miner will use all cores of the machine. Setting a thread +// count below zero is allowed and will cause the miner to idle, without any +// work being done. +func (ethash *Ethash) SetThreads(threads int) { + ethash.lock.Lock() + defer ethash.lock.Unlock() + + // If we're running a shared PoW, set the thread count on that instead + if ethash.shared != nil { + ethash.shared.SetThreads(threads) + return + } + // Update the threads and ping any running seal to pull in any changes + ethash.threads = threads + select { + case ethash.update <- struct{}{}: + default: + } +} + // Hashrate implements PoW, returning the measured rate of the search invocations // per second over the last minute. func (ethash *Ethash) Hashrate() float64 { return ethash.hashrate.Rate1() } -// EthashSeedHash is the seed to use for generating a vrification cache and the -// mining dataset. -func EthashSeedHash(block uint64) []byte { +// APIs implements consensus.Engine, returning the user facing RPC APIs. Currently +// that is empty. +func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API { + return nil +} + +// SeedHash is the seed to use for generating a verification cache and the mining +// dataset. +func SeedHash(block uint64) []byte { return seedHash(block) } diff --git a/consensus/ethash/ethash_test.go b/consensus/ethash/ethash_test.go new file mode 100644 index 000000000..b3a2f32f7 --- /dev/null +++ b/consensus/ethash/ethash_test.go @@ -0,0 +1,40 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package ethash + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/core/types" +) + +// Tests that ethash works correctly in test mode. +func TestTestMode(t *testing.T) { + head := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} + + ethash := NewTester() + block, err := ethash.Seal(nil, types.NewBlockWithHeader(head), nil) + if err != nil { + t.Fatalf("failed to seal block: %v", err) + } + head.Nonce = types.EncodeNonce(block.Nonce()) + head.MixDigest = block.MixDigest() + if err := ethash.VerifySeal(nil, head); err != nil { + t.Fatalf("unexpected verification error: %v", err) + } +} diff --git a/consensus/ethash/sealer.go b/consensus/ethash/sealer.go new file mode 100644 index 000000000..784e8f649 --- /dev/null +++ b/consensus/ethash/sealer.go @@ -0,0 +1,149 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package ethash + +import ( + crand "crypto/rand" + "math" + "math/big" + "math/rand" + "runtime" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +// Seal implements consensus.Engine, attempting to find a nonce that satisfies +// the block's difficulty requirements. +func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) { + // If we're running a fake PoW, simply return a 0 nonce immediately + if ethash.fakeMode { + header := block.Header() + header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{} + return block.WithSeal(header), nil + } + // If we're running a shared PoW, delegate sealing to it + if ethash.shared != nil { + return ethash.shared.Seal(chain, block, stop) + } + // Create a runner and the multiple search threads it directs + abort := make(chan struct{}) + found := make(chan *types.Block) + + ethash.lock.Lock() + threads := ethash.threads + if ethash.rand == nil { + seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + ethash.lock.Unlock() + return nil, err + } + ethash.rand = rand.New(rand.NewSource(seed.Int64())) + } + ethash.lock.Unlock() + if threads == 0 { + threads = runtime.NumCPU() + } + if threads < 0 { + threads = 0 // Allows disabling local mining without extra logic around local/remote + } + var pend sync.WaitGroup + for i := 0; i < threads; i++ { + pend.Add(1) + go func(id int, nonce uint64) { + defer pend.Done() + ethash.mine(block, id, nonce, abort, found) + }(i, uint64(ethash.rand.Int63())) + } + // Wait until sealing is terminated or a nonce is found + var result *types.Block + select { + case <-stop: + // Outside abort, stop all miner threads + close(abort) + case result = <-found: + // One of the threads found a block, abort all others + close(abort) + case <-ethash.update: + // Thread count was changed on user request, restart + close(abort) + pend.Wait() + return ethash.Seal(chain, block, stop) + } + // Wait for all miners to terminate and return the block + pend.Wait() + return result, nil +} + +// mine is the actual proof-of-work miner that searches for a nonce starting from +// seed that results in correct final block difficulty. +func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) { + // Extract some data from the header + var ( + header = block.Header() + hash = header.HashNoNonce().Bytes() + target = new(big.Int).Div(maxUint256, header.Difficulty) + + number = header.Number.Uint64() + dataset = ethash.dataset(number) + ) + // Start generating random nonces until we abort or find a good one + var ( + attempts = int64(0) + nonce = seed + ) + logger := log.New("miner", id) + logger.Trace("Started ethash search for new nonces", "seed", seed) + for { + select { + case <-abort: + // Mining terminated, update stats and abort + logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed) + ethash.hashrate.Mark(attempts) + return + + default: + // We don't have to update hash rate on every nonce, so update after after 2^X nonces + attempts++ + if (attempts % (1 << 15)) == 0 { + ethash.hashrate.Mark(attempts) + attempts = 0 + } + // Compute the PoW value of this nonce + digest, result := hashimotoFull(dataset, hash, nonce) + if new(big.Int).SetBytes(result).Cmp(target) <= 0 { + // Correct nonce found, create a new header with it + header = types.CopyHeader(header) + header.Nonce = types.EncodeNonce(nonce) + header.MixDigest = common.BytesToHash(digest) + + // Seal and return a block (if still needed) + select { + case found <- block.WithSeal(header): + logger.Trace("Ethash nonce found and reported", "attempts", nonce-seed, "nonce", nonce) + case <-abort: + logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce) + } + return + } + nonce++ + } + } +} diff --git a/pow/xor.go b/consensus/ethash/xor.go index 558aeb469..90e232746 100644 --- a/pow/xor.go +++ b/consensus/ethash/xor.go @@ -4,7 +4,7 @@ // Source: https://golang.org/src/crypto/cipher/xor.go -package pow +package ethash import ( "runtime" diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go new file mode 100644 index 000000000..67d4adb44 --- /dev/null +++ b/consensus/misc/dao.go @@ -0,0 +1,85 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package misc + +import ( + "bytes" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +var ( + // ErrBadProDAOExtra is returned if a header doens't support the DAO fork on a + // pro-fork client. + ErrBadProDAOExtra = errors.New("bad DAO pro-fork extra-data") + + // ErrBadNoDAOExtra is returned if a header does support the DAO fork on a no- + // fork client. + ErrBadNoDAOExtra = errors.New("bad DAO no-fork extra-data") +) + +// VerifyDAOHeaderExtraData validates the extra-data field of a block header to +// ensure it conforms to DAO hard-fork rules. +// +// DAO hard-fork extension to the header validity: +// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range +// with the fork specific extra-data set +// b) if the node is pro-fork, require blocks in the specific range to have the +// unique extra-data set. +func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error { + // Short circuit validation if the node doesn't care about the DAO fork + if config.DAOForkBlock == nil { + return nil + } + // Make sure the block is within the fork's modified extra-data range + limit := new(big.Int).Add(config.DAOForkBlock, params.DAOForkExtraRange) + if header.Number.Cmp(config.DAOForkBlock) < 0 || header.Number.Cmp(limit) >= 0 { + return nil + } + // Depending whether we support or oppose the fork, validate the extra-data contents + if config.DAOForkSupport { + if !bytes.Equal(header.Extra, params.DAOForkBlockExtra) { + return ErrBadProDAOExtra + } + } else { + if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { + return ErrBadNoDAOExtra + } + } + // All ok, header has the same extra-data we expect + return nil +} + +// ApplyDAOHardFork modifies the state database according to the DAO hard-fork +// rules, transferring all balances of a set of DAO accounts to a single refund +// contract. +func ApplyDAOHardFork(statedb *state.StateDB) { + // Retrieve the contract to refund balances into + if !statedb.Exist(params.DAORefundContract) { + statedb.CreateAccount(params.DAORefundContract) + } + + // Move every DAO account and extra-balance account funds into the refund contract + for _, addr := range params.DAODrainList() { + statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr)) + statedb.SetBalance(addr, new(big.Int)) + } +} diff --git a/consensus/misc/forks.go b/consensus/misc/forks.go new file mode 100644 index 000000000..4a5e7c37e --- /dev/null +++ b/consensus/misc/forks.go @@ -0,0 +1,43 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package misc + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +// VerifyForkHashes verifies that blocks conforming to network hard-forks do have +// the correct hashes, to avoid clients going off on different chains. This is an +// optional feature. +func VerifyForkHashes(config *params.ChainConfig, header *types.Header, uncle bool) error { + // We don't care about uncles + if uncle { + return nil + } + // If the homestead reprice hash is set, validate it + if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { + if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() { + return fmt.Errorf("homestead gas reprice fork: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash) + } + } + // All ok, return + return nil +} diff --git a/console/console_test.go b/console/console_test.go index d5010b907..0fc0e7051 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -21,17 +21,16 @@ import ( "errors" "fmt" "io/ioutil" - "math/big" "os" "strings" "testing" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/internal/jsre" "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" ) const ( @@ -92,14 +91,14 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { } // Create a networkless protocol stack and start an Ethereum service within - stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance, NoDiscovery: true}) + stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance}) if err != nil { t.Fatalf("failed to create node: %v", err) } ethConf := ð.Config{ - ChainConfig: ¶ms.ChainConfig{HomesteadBlock: new(big.Int), ChainId: new(big.Int)}, - Etherbase: common.HexToAddress(testAddress), - PowTest: true, + Genesis: core.DevGenesisBlock(), + Etherbase: common.HexToAddress(testAddress), + PowTest: true, } if confOverride != nil { confOverride(ethConf) diff --git a/contracts/chequebook/cheque.go b/contracts/chequebook/cheque.go index 945e56e86..7e0f7eafc 100644 --- a/contracts/chequebook/cheque.go +++ b/contracts/chequebook/cheque.go @@ -26,6 +26,7 @@ package chequebook import ( "bytes" + "context" "crypto/ecdsa" "encoding/json" "fmt" @@ -43,7 +44,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/swarm/services/swap/swap" - "golang.org/x/net/context" ) // TODO(zelig): watch peer solvency and notify of bouncing cheques diff --git a/contracts/chequebook/cheque_test.go b/contracts/chequebook/cheque_test.go index 4029fd549..d5b343024 100644 --- a/contracts/chequebook/cheque_test.go +++ b/contracts/chequebook/cheque_test.go @@ -42,11 +42,11 @@ var ( ) func newTestBackend() *backends.SimulatedBackend { - return backends.NewSimulatedBackend( - core.GenesisAccount{Address: addr0, Balance: big.NewInt(1000000000)}, - core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000000)}, - core.GenesisAccount{Address: addr2, Balance: big.NewInt(1000000000)}, - ) + return backends.NewSimulatedBackend(core.GenesisAlloc{ + addr0: {Balance: big.NewInt(1000000000)}, + addr1: {Balance: big.NewInt(1000000000)}, + addr2: {Balance: big.NewInt(1000000000)}, + }) } func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) { diff --git a/contracts/ens/ens_test.go b/contracts/ens/ens_test.go index 373ce2e30..5faa9b1ad 100644 --- a/contracts/ens/ens_test.go +++ b/contracts/ens/ens_test.go @@ -34,7 +34,7 @@ var ( ) func TestENS(t *testing.T) { - contractBackend := backends.NewSimulatedBackend(core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000000)}) + contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}) transactOpts := bind.NewKeyedTransactor(key) // Workaround for bug estimating gas in the call to Register transactOpts.GasLimit = big.NewInt(1000000) diff --git a/contracts/release/contract_test.go b/contracts/release/contract_test.go index 11a039992..348e9aabe 100644 --- a/contracts/release/contract_test.go +++ b/contracts/release/contract_test.go @@ -35,11 +35,11 @@ func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.Privat key, _ := crypto.GenerateKey() auth := bind.NewKeyedTransactor(key) - accounts := []core.GenesisAccount{{Address: auth.From, Balance: big.NewInt(10000000000)}} + alloc := core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}} for _, key := range prefund { - accounts = append(accounts, core.GenesisAccount{Address: crypto.PubkeyToAddress(key.PublicKey), Balance: big.NewInt(10000000000)}) + alloc[crypto.PubkeyToAddress(key.PublicKey)] = core.GenesisAccount{Balance: big.NewInt(10000000000)} } - sim := backends.NewSimulatedBackend(accounts...) + sim := backends.NewSimulatedBackend(alloc) // Deploy a version oracle contract, commit and return _, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From}) diff --git a/contracts/release/release.go b/contracts/release/release.go index 613e62aa9..28a35381d 100644 --- a/contracts/release/release.go +++ b/contracts/release/release.go @@ -20,6 +20,7 @@ package release //go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go import ( + "context" "fmt" "strings" "time" @@ -33,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) // Interval to check for new releases @@ -116,47 +116,49 @@ func (r *ReleaseService) checker() { for { select { - // If the time arrived, check for a new release case <-timer.C: // Rechedule the timer before continuing timer.Reset(releaseRecheckInterval) - - // Retrieve the current version, and handle missing contracts gracefully - ctx, _ := context.WithTimeout(context.Background(), time.Second*5) - opts := &bind.CallOpts{Context: ctx} - version, err := r.oracle.CurrentVersion(opts) - if err != nil { - if err == bind.ErrNoCode { - log.Debug("Release oracle not found", "contract", r.config.Oracle) - continue - } - log.Error("Failed to retrieve current release", "err", err) - continue - } - // Version was successfully retrieved, notify if newer than ours - if version.Major > r.config.Major || - (version.Major == r.config.Major && version.Minor > r.config.Minor) || - (version.Major == r.config.Major && version.Minor == r.config.Minor && version.Patch > r.config.Patch) { - - warning := fmt.Sprintf("Client v%d.%d.%d-%x seems older than the latest upstream release v%d.%d.%d-%x", - r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4]) - howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases") - separator := strings.Repeat("-", len(warning)) - - log.Warn(separator) - log.Warn(warning) - log.Warn(howtofix) - log.Warn(separator) - } else { - log.Debug("Client seems up to date with upstream", - "local", fmt.Sprintf("v%d.%d.%d-%x", r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4]), - "upstream", fmt.Sprintf("v%d.%d.%d-%x", version.Major, version.Minor, version.Patch, version.Commit[:4])) - } - - // If termination was requested, return + r.checkVersion() case errc := <-r.quit: errc <- nil return } } } + +func (r *ReleaseService) checkVersion() { + // Retrieve the current version, and handle missing contracts gracefully + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + opts := &bind.CallOpts{Context: ctx} + defer cancel() + + version, err := r.oracle.CurrentVersion(opts) + if err != nil { + if err == bind.ErrNoCode { + log.Debug("Release oracle not found", "contract", r.config.Oracle) + } else { + log.Error("Failed to retrieve current release", "err", err) + } + return + } + // Version was successfully retrieved, notify if newer than ours + if version.Major > r.config.Major || + (version.Major == r.config.Major && version.Minor > r.config.Minor) || + (version.Major == r.config.Major && version.Minor == r.config.Minor && version.Patch > r.config.Patch) { + + warning := fmt.Sprintf("Client v%d.%d.%d-%x seems older than the latest upstream release v%d.%d.%d-%x", + r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4]) + howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases") + separator := strings.Repeat("-", len(warning)) + + log.Warn(separator) + log.Warn(warning) + log.Warn(howtofix) + log.Warn(separator) + } else { + log.Debug("Client seems up to date with upstream", + "local", fmt.Sprintf("v%d.%d.%d-%x", r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4]), + "upstream", fmt.Sprintf("v%d.%d.%d-%x", version.Major, version.Minor, version.Patch, version.Commit[:4])) + } +} diff --git a/core/bench_test.go b/core/bench_test.go index 42d50a7b1..20676fc97 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -25,13 +25,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) func BenchmarkInsertChain_empty_memdb(b *testing.B) { @@ -166,13 +166,17 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Generate a chain of b.N blocks using the supplied block // generator function. - genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds}) - chain, _ := GenerateChain(params.TestChainConfig, genesis, db, b.N, gen) + gspec := Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}}, + } + genesis := gspec.MustCommit(db) + chain, _ := GenerateChain(gspec.Config, genesis, db, b.N, gen) // Time the insertion of the new chain. // State and blocks are stored in the same DB. evmux := new(event.TypeMux) - chainman, _ := NewBlockChain(db, ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, pow.FakePow{}, evmux, vm.Config{}) + chainman, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), evmux, vm.Config{}) defer chainman.Stop() b.ReportAllocs() b.ResetTimer() @@ -282,7 +286,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, testChainConfig(), pow.FakePow{}, new(event.TypeMux), vm.Config{}) + chain, err := NewBlockChain(db, params.TestChainConfig, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) if err != nil { b.Fatalf("error creating chain: %v", err) } diff --git a/core/block_validator.go b/core/block_validator.go index f93a9f40b..4f85df007 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -19,22 +19,12 @@ package core import ( "fmt" "math/big" - "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/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" - "gopkg.in/fatih/set.v0" -) - -var ( - ExpDiffPeriod = big.NewInt(100000) - big10 = big.NewInt(10) - bigMinus99 = big.NewInt(-99) ) // BlockValidator is responsible for validating block headers, uncles and @@ -44,66 +34,47 @@ var ( type BlockValidator struct { config *params.ChainConfig // Chain configuration options bc *BlockChain // Canonical block chain - Pow pow.PoW // Proof of work used for validating + engine consensus.Engine // Consensus engine used for validating } // NewBlockValidator returns a new block validator which is safe for re-use -func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { +func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator { validator := &BlockValidator{ config: config, - Pow: pow, + engine: engine, bc: blockchain, } return validator } -// ValidateBlock validates the given block's header and uncles and verifies the -// the block header's transaction and uncle roots. -// -// ValidateBlock does not validate the header's pow. The pow work validated -// separately so we can process them in parallel. -// -// ValidateBlock also validates and makes sure that any previous state (or present) -// state that might or might not be present is checked to make sure that fast -// sync has done it's job proper. This prevents the block validator from accepting -// false positives where a header is present but the state is not. -func (v *BlockValidator) ValidateBlock(block *types.Block) error { +// ValidateBody validates the given block's uncles and verifies the the block +// header's transaction and uncle roots. The headers are assumed to be already +// validated at this point. +func (v *BlockValidator) ValidateBody(block *types.Block) error { + // Check whether the block's known, and if not, that it's linkable if v.bc.HasBlock(block.Hash()) { if _, err := state.New(block.Root(), v.bc.chainDb); err == nil { - return &KnownBlockError{block.Number(), block.Hash()} + return ErrKnownBlock } } parent := v.bc.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return ParentError(block.ParentHash()) + return consensus.ErrUnknownAncestor } if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil { - return ParentError(block.ParentHash()) + return consensus.ErrUnknownAncestor } - + // Header validity is known at this point, check the uncles and transactions header := block.Header() - // validate the block header - if err := ValidateHeader(v.config, v.Pow, header, parent.Header(), false, false); err != nil { + if err := v.engine.VerifyUncles(v.bc, block); err != nil { return err } - // verify the uncles are correctly rewarded - if err := v.VerifyUncles(block, parent); err != nil { - return err + if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { + return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash) } - - // Verify UncleHash before running other uncle validations - unclesSha := types.CalcUncleHash(block.Uncles()) - if unclesSha != header.UncleHash { - return fmt.Errorf("invalid uncles root hash (remote: %x local: %x)", header.UncleHash, unclesSha) - } - - // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]])) - // can be used by light clients to make sure they've received the correct Txs - txSha := types.DeriveSha(block.Transactions()) - if txSha != header.TxHash { - return fmt.Errorf("invalid transaction root hash (remote: %x local: %x)", header.TxHash, txSha) + if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash { + return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) } - return nil } @@ -111,10 +82,10 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { // transition, such as amount of used gas, the receipt roots and the state root // itself. ValidateState returns a database batch if the validation was a success // otherwise nil and an error is returned. -func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) { +func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) error { header := block.Header() if block.GasUsed().Cmp(usedGas) != 0 { - return ValidationError(fmt.Sprintf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas)) + return fmt.Errorf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas) } // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. @@ -135,222 +106,6 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat return nil } -// VerifyUncles verifies the given block's uncles and applies the Ethereum -// consensus rules to the various block headers included; it will return an -// error if any of the included uncle headers were invalid. It returns an error -// if the validation failed. -func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error { - // validate that there are at most 2 uncles included in this block - if len(block.Uncles()) > 2 { - return ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles())) - } - - uncles := set.New() - ancestors := make(map[common.Hash]*types.Block) - for _, ancestor := range v.bc.GetBlocksFromHash(block.ParentHash(), 7) { - ancestors[ancestor.Hash()] = ancestor - // Include ancestors uncles in the uncle set. Uncles must be unique. - for _, uncle := range ancestor.Uncles() { - uncles.Add(uncle.Hash()) - } - } - ancestors[block.Hash()] = block - uncles.Add(block.Hash()) - - for i, uncle := range block.Uncles() { - hash := uncle.Hash() - if uncles.Has(hash) { - // Error not unique - return UncleError("uncle[%d](%x) not unique", i, hash[:4]) - } - uncles.Add(hash) - - if ancestors[hash] != nil { - branch := fmt.Sprintf(" O - %x\n |\n", block.Hash()) - for h := range ancestors { - branch += fmt.Sprintf(" O - %x\n |\n", h) - } - log.Warn(branch) - return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) - } - - if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() { - return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) - } - - if err := ValidateHeader(v.config, v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { - return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) - } - } - - return nil -} - -// ValidateHeader validates the given header and, depending on the pow arg, -// checks the proof of work of the given header. Returns an error if the -// validation failed. -func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error { - // Short circuit if the parent is missing. - if parent == nil { - return ParentError(header.ParentHash) - } - // Short circuit if the header's already known or its parent is missing - if v.bc.HasHeader(header.Hash()) { - return nil - } - return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) -} - -// Validates a header. Returns an error if the header is invalid. -// -// See YP section 4.3.4. "Block Header Validity" -func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { - if uint64(len(header.Extra)) > params.MaximumExtraDataSize { - return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) - } - - if uncle { - if header.Time.Cmp(math.MaxBig256) == 1 { - return BlockTSTooBigErr - } - } else { - if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 { - return BlockFutureErr - } - } - if header.Time.Cmp(parent.Time) != 1 { - return BlockEqualTSErr - } - - expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) - if expd.Cmp(header.Difficulty) != 0 { - return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd) - } - - a := new(big.Int).Set(parent.GasLimit) - a = a.Sub(a, header.GasLimit) - a.Abs(a) - b := new(big.Int).Set(parent.GasLimit) - b = b.Div(b, params.GasLimitBoundDivisor) - if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) { - return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b) - } - - num := new(big.Int).Set(parent.Number) - num.Sub(header.Number, num) - if num.Cmp(big.NewInt(1)) != 0 { - return BlockNumberErr - } - - if checkPow { - // Verify the nonce of the header. Return an error if it's not valid - if err := pow.Verify(types.NewBlockWithHeader(header)); err != nil { - return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()} - } - } - // If all checks passed, validate the extra-data field for hard forks - if err := ValidateDAOHeaderExtraData(config, header); err != nil { - return err - } - if !uncle && config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { - if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() { - return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash) - } - } - return nil -} - -// 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 CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { - if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { - return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff) - } else { - return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff) - } -} - -func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki - // algorithm: - // diff = (parent_diff + - // (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) - // ) + 2^(periodCount - 2) - - bigTime := new(big.Int).SetUint64(time) - bigParentTime := new(big.Int).SetUint64(parentTime) - - // holds intermediate values to make the algo easier to read & audit - x := new(big.Int) - y := new(big.Int) - - // 1 - (block_timestamp -parent_timestamp) // 10 - x.Sub(bigTime, bigParentTime) - x.Div(x, big10) - x.Sub(common.Big1, x) - - // max(1 - (block_timestamp - parent_timestamp) // 10, -99))) - if x.Cmp(bigMinus99) < 0 { - x.Set(bigMinus99) - } - - // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) - y.Div(parentDiff, params.DifficultyBoundDivisor) - x.Mul(y, x) - x.Add(parentDiff, x) - - // minimum difficulty can ever be (before exponential factor) - if x.Cmp(params.MinimumDifficulty) < 0 { - x.Set(params.MinimumDifficulty) - } - - // for the exponential factor - periodCount := new(big.Int).Add(parentNumber, common.Big1) - periodCount.Div(periodCount, ExpDiffPeriod) - - // the exponential factor, commonly referred to as "the bomb" - // diff = diff + 2^(periodCount - 2) - if periodCount.Cmp(common.Big1) > 0 { - y.Sub(periodCount, common.Big2) - y.Exp(common.Big2, y, nil) - x.Add(x, y) - } - - return x -} - -func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { - diff := new(big.Int) - adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor) - bigTime := new(big.Int) - bigParentTime := new(big.Int) - - bigTime.SetUint64(time) - bigParentTime.SetUint64(parentTime) - - if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 { - diff.Add(parentDiff, adjust) - } else { - diff.Sub(parentDiff, adjust) - } - if diff.Cmp(params.MinimumDifficulty) < 0 { - diff.Set(params.MinimumDifficulty) - } - - periodCount := new(big.Int).Add(parentNumber, common.Big1) - periodCount.Div(periodCount, ExpDiffPeriod) - if periodCount.Cmp(common.Big1) > 0 { - // diff = diff + 2^(periodCount - 2) - expDiff := periodCount.Sub(periodCount, common.Big2) - expDiff.Exp(common.Big2, expDiff, nil) - diff.Add(diff, expDiff) - diff = math.BigMax(diff, params.MinimumDifficulty) - } - - return diff -} - // CalcGasLimit computes the gas limit of the next block after parent. // The result may be modified by the caller. // This is miner strategy, not consensus protocol. diff --git a/core/block_validator_test.go b/core/block_validator_test.go index c13aa83db..abe1766b4 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -17,79 +17,179 @@ package core import ( - "fmt" - "math/big" + "runtime" "testing" + "time" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) -func testChainConfig() *params.ChainConfig { - return params.TestChainConfig - //return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} -} +// Tests that simple header verification works, for both good and bad blocks. +func TestHeaderVerification(t *testing.T) { + // Create a simple chain to verify + var ( + testdb, _ = ethdb.NewMemDatabase() + gspec = &Genesis{Config: params.TestChainConfig} + genesis = gspec.MustCommit(testdb) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) + ) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces + chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) -func proc() (Validator, *BlockChain) { - db, _ := ethdb.NewMemDatabase() - var mux event.TypeMux + for i := 0; i < len(blocks); i++ { + for j, valid := range []bool{true, false} { + var results <-chan error - WriteTestNetGenesisBlock(db) - blockchain, err := NewBlockChain(db, testChainConfig(), pow.NewTestEthash(), &mux, vm.Config{}) - if err != nil { - fmt.Println(err) + if valid { + engine := ethash.NewFaker() + _, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) + } else { + engine := ethash.NewFakeFailer(headers[i].Number.Uint64()) + _, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) + } + // Wait for the verification result + select { + case result := <-results: + if (result == nil) != valid { + t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, result, valid) + } + case <-time.After(time.Second): + t.Fatalf("test %d.%d: verification timeout", i, j) + } + // Make sure no more data is returned + select { + case result := <-results: + t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result) + case <-time.After(25 * time.Millisecond): + } + } + chain.InsertChain(blocks[i : i+1]) } - return blockchain.validator, blockchain } -func TestNumber(t *testing.T) { - _, chain := proc() +// 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) } +func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVerification(t, 32) } + +func testHeaderConcurrentVerification(t *testing.T, threads int) { + // Create a simple chain to verify + var ( + testdb, _ = ethdb.NewMemDatabase() + gspec = &Genesis{Config: params.TestChainConfig} + genesis = gspec.MustCommit(testdb) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) + ) + headers := make([]*types.Header, len(blocks)) + seals := make([]bool, len(blocks)) - statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb) - cfg := testChainConfig() - header := makeHeader(cfg, chain.Genesis(), statedb) - header.Number = big.NewInt(3) - err := ValidateHeader(cfg, pow.FakePow{}, header, chain.Genesis().Header(), false, false) - if err != BlockNumberErr { - t.Errorf("expected block number error, got %q", err) + for i, block := range blocks { + headers[i] = block.Header() + seals[i] = true } + // Set the number of threads to verify on + old := runtime.GOMAXPROCS(threads) + defer runtime.GOMAXPROCS(old) - header = makeHeader(cfg, chain.Genesis(), statedb) - err = ValidateHeader(cfg, pow.FakePow{}, header, chain.Genesis().Header(), false, false) - if err == BlockNumberErr { - t.Errorf("didn't expect block number error") + // Run the header checker for the entire block chain at once both for a valid and + // also an invalid chain (enough if one arbitrary block is invalid). + for i, valid := range []bool{true, false} { + var results <-chan error + + if valid { + chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) + _, results = chain.engine.VerifyHeaders(chain, headers, seals) + } else { + chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), new(event.TypeMux), vm.Config{}) + _, results = chain.engine.VerifyHeaders(chain, headers, seals) + } + // Wait for all the verification results + checks := make(map[int]error) + for j := 0; j < len(blocks); j++ { + select { + case result := <-results: + checks[j] = result + + case <-time.After(time.Second): + t.Fatalf("test %d.%d: verification timeout", i, j) + } + } + // Check nonce check validity + for j := 0; j < len(blocks); j++ { + want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail + if (checks[j] == nil) != want { + t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want) + } + if !want { + // A few blocks after the first error may pass verification due to concurrent + // workers. We don't care about those in this test, just that the correct block + // errors out. + break + } + } + // 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): + } } } -func TestPutReceipt(t *testing.T) { - db, _ := ethdb.NewMemDatabase() - - var addr common.Address - addr[0] = 1 - var hash common.Hash - hash[0] = 2 - - receipt := new(types.Receipt) - receipt.Logs = []*types.Log{{ - Address: addr, - Topics: []common.Hash{hash}, - Data: []byte("hi"), - BlockNumber: 42, - TxHash: hash, - TxIndex: 0, - BlockHash: hash, - Index: 0, - }} - - WriteReceipts(db, types.Receipts{receipt}) - receipt = GetReceipt(db, common.Hash{}) - if receipt == nil { - t.Error("expected to get 1 receipt, got none.") +// Tests that aborting a header validation indeed prevents further checks from being +// run, as well as checks that no left-over goroutines are leaked. +func TestHeaderConcurrentAbortion2(t *testing.T) { testHeaderConcurrentAbortion(t, 2) } +func TestHeaderConcurrentAbortion8(t *testing.T) { testHeaderConcurrentAbortion(t, 8) } +func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion(t, 32) } + +func testHeaderConcurrentAbortion(t *testing.T, threads int) { + // Create a simple chain to verify + var ( + testdb, _ = ethdb.NewMemDatabase() + gspec = &Genesis{Config: params.TestChainConfig} + genesis = gspec.MustCommit(testdb) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil) + ) + headers := make([]*types.Header, len(blocks)) + seals := make([]bool, len(blocks)) + + for i, block := range blocks { + headers[i] = block.Header() + seals[i] = true + } + // Set the number of threads to verify on + old := runtime.GOMAXPROCS(threads) + defer runtime.GOMAXPROCS(old) + + // Start the verifications and immediately abort + chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), new(event.TypeMux), vm.Config{}) + abort, results := chain.engine.VerifyHeaders(chain, headers, seals) + close(abort) + + // Deplete the results channel + verified := 0 + for depleted := false; !depleted; { + select { + case result := <-results: + if result != nil { + t.Errorf("header %d: validation failed: %v", verified, result) + } + verified++ + case <-time.After(50 * time.Millisecond): + depleted = true + } + } + // Check that abortion was honored by not processing too many POWs + if verified > 2*threads { + t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads) } } diff --git a/core/blockchain.go b/core/blockchain.go index 765a4b318..d6f2653ae 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" + "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/core/vm" @@ -39,7 +40,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/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/hashicorp/golang-lru" @@ -104,7 +104,7 @@ type BlockChain struct { procInterrupt int32 // interrupt signaler for block processing wg sync.WaitGroup // chain processing wait group for shutting down - pow pow.PoW + engine consensus.Engine processor Processor // block processor interface validator Validator // block and state validator interface vmConfig vm.Config @@ -113,9 +113,9 @@ type BlockChain struct { } // NewBlockChain returns a fully initialised block chain using information -// available in the database. It initialiser the default Ethereum Validator and +// available in the database. It initialises the default Ethereum Validator and // Processor. -func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux, vmConfig vm.Config) (*BlockChain, error) { +func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, mux *event.TypeMux, vmConfig vm.Config) (*BlockChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) @@ -131,25 +131,22 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P bodyRLPCache: bodyRLPCache, blockCache: blockCache, futureBlocks: futureBlocks, - pow: pow, + engine: engine, vmConfig: vmConfig, badBlocks: badBlocks, } - bc.SetValidator(NewBlockValidator(config, bc, pow)) - bc.SetProcessor(NewStateProcessor(config, bc)) + bc.SetValidator(NewBlockValidator(config, bc, engine)) + bc.SetProcessor(NewStateProcessor(config, bc, engine)) - gv := func() HeaderValidator { return bc.Validator() } var err error - bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt) + bc.hc, err = NewHeaderChain(chainDb, config, engine, bc.getProcInterrupt) if err != nil { return nil, err } - bc.genesisBlock = bc.GetBlockByNumber(0) if bc.genesisBlock == nil { return nil, ErrNoGenesis } - if err := bc.loadLastState(); err != nil { return nil, err } @@ -182,16 +179,25 @@ func (self *BlockChain) loadLastState() error { head := GetHeadBlockHash(self.chainDb) if head == (common.Hash{}) { // Corrupt or empty database, init from scratch - self.Reset() - } else { - if block := self.GetBlockByHash(head); block != nil { - // Block found, set as the current head - self.currentBlock = block - } else { - // Corrupt or empty database, init from scratch - self.Reset() - } + log.Warn("Empty database, resetting chain") + return self.Reset() } + // Make sure the entire head block is available + currentBlock := self.GetBlockByHash(head) + if currentBlock == nil { + // Corrupt or empty database, init from scratch + log.Warn("Head block missing, resetting chain", "hash", head) + return self.Reset() + } + // Make sure the state associated with the block is available + if _, err := state.New(currentBlock.Root(), self.chainDb); err != nil { + // Dangling block without a state associated, init from scratch + log.Warn("Head state missing, resetting chain", "number", currentBlock.Number(), "hash", currentBlock.Hash()) + return self.Reset() + } + // Everything seems to be fine, set as the head block + self.currentBlock = currentBlock + // Restore the last known head header currentHeader := self.currentBlock.Header() if head := GetHeadHeaderHash(self.chainDb); head != (common.Hash{}) { @@ -200,6 +206,7 @@ func (self *BlockChain) loadLastState() error { } } self.hc.SetCurrentHeader(currentHeader) + // Restore the last known head fast block self.currentFastBlock = self.currentBlock if head := GetHeadFastBlockHash(self.chainDb); head != (common.Hash{}) { @@ -223,9 +230,6 @@ func (self *BlockChain) loadLastState() error { log.Info("Loaded most recent local full block", "number", self.currentBlock.Number(), "hash", self.currentBlock.Hash(), "td", blockTd) log.Info("Loaded most recent local fast block", "number", self.currentFastBlock.Number(), "hash", self.currentFastBlock.Hash(), "td", fastTd) - // Try to be smart and issue a pow verification for the head to pre-generate caches - go self.pow.Verify(types.NewBlockWithHeader(currentHeader)) - return nil } @@ -233,14 +237,18 @@ func (self *BlockChain) loadLastState() error { // above the new head will be deleted and the new one set. In the case of blocks // though, the head may be further rewound if block bodies are missing (non-archive // nodes after a fast sync). -func (bc *BlockChain) SetHead(head uint64) { +func (bc *BlockChain) SetHead(head uint64) error { + log.Warn("Rewinding blockchain", "target", head) + bc.mu.Lock() defer bc.mu.Unlock() + // Rewind the header chain, deleting all block bodies until then delFn := func(hash common.Hash, num uint64) { DeleteBody(bc.chainDb, hash, num) } bc.hc.SetHead(head, delFn) + currentHeader := bc.hc.CurrentHeader() // Clear out any stale content from the caches bc.bodyCache.Purge() @@ -248,29 +256,34 @@ func (bc *BlockChain) SetHead(head uint64) { bc.blockCache.Purge() bc.futureBlocks.Purge() - // Update all computed fields to the new head - currentHeader := bc.hc.CurrentHeader() + // Rewind the block chain, ensuring we don't end up with a stateless head block if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() { bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()) } + if bc.currentBlock != nil { + if _, err := state.New(bc.currentBlock.Root(), bc.chainDb); err != nil { + // Rewound state missing, rolled back to before pivot, reset to genesis + bc.currentBlock = nil + } + } + // Rewind the fast block in a simpleton way to the target head if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() { bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()) } - + // If either blocks reached nil, reset to the genesis state if bc.currentBlock == nil { bc.currentBlock = bc.genesisBlock } if bc.currentFastBlock == nil { bc.currentFastBlock = bc.genesisBlock } - if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil { log.Crit("Failed to reset head full block", "err", err) } if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil { log.Crit("Failed to reset head fast block", "err", err) } - bc.loadLastState() + return bc.loadLastState() } // FastSyncCommitHead sets the current head block to the one defined by the hash @@ -364,9 +377,6 @@ func (self *BlockChain) Processor() Processor { return self.processor } -// AuxValidator returns the auxiliary validator (Proof of work atm) -func (self *BlockChain) AuxValidator() pow.PoW { return self.pow } - // State returns a new mutable state based on the current HEAD block. func (self *BlockChain) State() (*state.StateDB, error) { return self.StateAt(self.CurrentBlock().Root()) @@ -378,16 +388,17 @@ func (self *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { } // Reset purges the entire blockchain, restoring it to its genesis state. -func (bc *BlockChain) Reset() { - bc.ResetWithGenesisBlock(bc.genesisBlock) +func (bc *BlockChain) Reset() error { + return bc.ResetWithGenesisBlock(bc.genesisBlock) } // ResetWithGenesisBlock purges the entire blockchain, restoring it to the // specified genesis state. -func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) { +func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { // Dump the entire block chain and purge the caches - bc.SetHead(0) - + if err := bc.SetHead(0); err != nil { + return err + } bc.mu.Lock() defer bc.mu.Unlock() @@ -404,6 +415,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) { bc.hc.SetGenesis(bc.genesisBlock.Header()) bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) bc.currentFastBlock = bc.genesisBlock + + return nil } // Export writes the active chain to the given writer. @@ -790,12 +803,15 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain } // Update the head fast sync block if better self.mu.Lock() + head := blockChain[len(errs)-1] - if self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()).Cmp(self.GetTd(head.Hash(), head.NumberU64())) < 0 { - if err := WriteHeadFastBlockHash(self.chainDb, head.Hash()); err != nil { - log.Crit("Failed to update head fast block hash", "err", err) + if td := self.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case + if self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()).Cmp(td) < 0 { + if err := WriteHeadFastBlockHash(self.chainDb, head.Hash()); err != nil { + log.Crit("Failed to update head fast block hash", "err", err) + } + self.currentFastBlock = head } - self.currentFastBlock = head } self.mu.Unlock() @@ -815,7 +831,7 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err // Calculate the total difficulty of the block ptd := self.GetTd(block.ParentHash(), block.NumberU64()-1) if ptd == nil { - return NonStatTy, ParentError(block.ParentHash()) + return NonStatTy, consensus.ErrUnknownAncestor } // Make sure no inconsistent state is leaked during insertion self.mu.Lock() @@ -881,59 +897,57 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { stats = insertStats{startTime: mclock.Now()} events = make([]interface{}, 0, len(chain)) coalescedLogs []*types.Log - nonceChecked = make([]bool, len(chain)) ) + // Start the parallel header verifier + headers := make([]*types.Header, len(chain)) + seals := make([]bool, len(chain)) - // Start the parallel nonce verifier. - nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain) - defer close(nonceAbort) + for i, block := range chain { + headers[i] = block.Header() + seals[i] = true + } + abort, results := self.engine.VerifyHeaders(self, headers, seals) + defer close(abort) + // Iterate over the blocks and insert when the verifier permits for i, block := range chain { + // If the chain is terminating, stop processing blocks if atomic.LoadInt32(&self.procInterrupt) == 1 { log.Debug("Premature abort during blocks processing") break } - bstart := time.Now() - // Wait for block i's nonce to be verified before processing - // its state transition. - for !nonceChecked[i] { - r := <-nonceResults - nonceChecked[r.index] = true - if !r.valid { - invalid := chain[r.index] - return r.index, &BlockNonceErr{Hash: invalid.Hash(), Number: invalid.Number(), Nonce: invalid.Nonce()} - } + // If the header is a banned one, straight out abort + if BadHashes[block.Hash()] { + self.reportBlock(block, nil, ErrBlacklistedHash) + return i, ErrBlacklistedHash } + // Wait for the block's verification to complete + bstart := time.Now() - if BadHashes[block.Hash()] { - err := BadHashError(block.Hash()) - self.reportBlock(block, nil, err) - return i, err + err := <-results + if err == nil { + err = self.Validator().ValidateBody(block) } - // Stage 1 validation of the block using the chain's validator - // interface. - err := self.Validator().ValidateBlock(block) if err != nil { - if IsKnownBlockErr(err) { + if err == ErrKnownBlock { stats.ignored++ continue } - if err == BlockFutureErr { + if err == consensus.ErrFutureBlock { // Allow up to MaxFuture second in the future blocks. If this limit // is exceeded the chain is discarded and processed at a later time // if given. max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks) - if block.Time().Cmp(max) == 1 { - return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max) + if block.Time().Cmp(max) > 0 { + return i, fmt.Errorf("future block: %v > %v", block.Time(), max) } - self.futureBlocks.Add(block.Hash(), block) stats.queued++ continue } - if IsParentErr(err) && self.futureBlocks.Contains(block.ParentHash()) { + if err == consensus.ErrUnknownAncestor && self.futureBlocks.Contains(block.ParentHash()) { self.futureBlocks.Add(block.Hash(), block) stats.queued++ continue @@ -1288,6 +1302,11 @@ Error: %v // of the header retrieval mechanisms already need to verify nonces, as well as // because nonces can be verified sparsely, not needing to check each. func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { + start := time.Now() + if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil { + return i, err + } + // Make sure only one thread manipulates the chain at once self.chainmu.Lock() defer self.chainmu.Unlock() @@ -1303,7 +1322,7 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) return err } - return self.hc.InsertHeaderChain(chain, checkFreq, whFunc) + return self.hc.InsertHeaderChain(chain, whFunc, start) } // writeHeader writes a header into the local chain, given that its parent is @@ -1379,3 +1398,6 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header { // Config retrieves the blockchain's chain configuration. func (self *BlockChain) Config() *params.ChainConfig { return self.config } + +// Engine retrieves the blockchain's consensus engine. +func (self *BlockChain) Engine() consensus.Engine { return self.engine } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 2f06f0735..7505208e1 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -20,14 +20,11 @@ import ( "fmt" "math/big" "math/rand" - "os" - "path/filepath" - "runtime" - "strconv" "testing" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -35,25 +32,25 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" - "github.com/ethereum/go-ethereum/rlp" - "github.com/hashicorp/golang-lru" ) -func init() { - runtime.GOMAXPROCS(runtime.NumCPU()) -} - -func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { - var eventMux event.TypeMux - WriteTestNetGenesisBlock(db) - blockchain, err := NewBlockChain(db, testChainConfig(), pow.NewTestEthash(), &eventMux, vm.Config{}) +// newTestBlockChain creates a blockchain without validation. +func newTestBlockChain(fake bool) *BlockChain { + db, _ := ethdb.NewMemDatabase() + gspec := &Genesis{ + Config: params.TestChainConfig, + Difficulty: big.NewInt(1), + } + gspec.MustCommit(db) + engine := ethash.NewFullFaker() + if !fake { + engine = ethash.NewTester() + } + blockchain, err := NewBlockChain(db, gspec.Config, engine, new(event.TypeMux), vm.Config{}) if err != nil { - t.Error("failed creating blockchain:", err) - t.FailNow() - return nil + panic(err) } - + blockchain.SetValidator(bproc{}) return blockchain } @@ -124,9 +121,12 @@ func printChain(bc *BlockChain) { func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { for _, block := range chain { // Try and process the block - err := blockchain.Validator().ValidateBlock(block) + err := blockchain.engine.VerifyHeader(blockchain, block.Header(), true) + if err == nil { + err = blockchain.validator.ValidateBody(block) + } if err != nil { - if IsKnownBlockErr(err) { + if err == ErrKnownBlock { continue } return err @@ -140,7 +140,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.Validator().ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas) + err = blockchain.validator.ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas) if err != nil { blockchain.reportBlock(block, receipts, err) return err @@ -159,7 +159,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error { for _, header := range chain { // Try and validate the header - if err := blockchain.Validator().ValidateHeader(header, blockchain.GetHeaderByHash(header.ParentHash), false); err != nil { + if err := blockchain.engine.VerifyHeader(blockchain, header, false); err != nil { return err } // Manually insert the header into the database, but don't reorganise (allows subsequent testing) @@ -171,21 +171,6 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error return nil } -func loadChain(fn string, t *testing.T) (types.Blocks, error) { - fh, err := os.OpenFile(filepath.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm) - if err != nil { - return nil, err - } - defer fh.Close() - - var chain types.Blocks - if err := rlp.Decode(fh, &chain); err != nil { - return nil, err - } - - return chain, nil -} - func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *testing.T) { _, err := blockchain.InsertChain(chain) if err != nil { @@ -196,12 +181,10 @@ func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t * } func TestLastBlock(t *testing.T) { - db, _ := ethdb.NewMemDatabase() - - bchain := theBlockChain(db, t) - block := makeBlockChain(bchain.CurrentBlock(), 1, db, 0)[0] + bchain := newTestBlockChain(false) + block := makeBlockChain(bchain.CurrentBlock(), 1, bchain.chainDb, 0)[0] bchain.insert(block) - if block.Hash() != GetHeadBlockHash(db) { + if block.Hash() != GetHeadBlockHash(bchain.chainDb) { t.Errorf("Write/Get HeadBlockHash failed") } } @@ -340,92 +323,9 @@ func testBrokenChain(t *testing.T, full bool) { } } -func TestChainInsertions(t *testing.T) { - t.Skip("Skipped: outdated test files") - - db, _ := ethdb.NewMemDatabase() - - chain1, err := loadChain("valid1", t) - if err != nil { - fmt.Println(err) - t.FailNow() - } - - chain2, err := loadChain("valid2", t) - if err != nil { - fmt.Println(err) - t.FailNow() - } - - blockchain := theBlockChain(db, t) - - const max = 2 - done := make(chan bool, max) - - go insertChain(done, blockchain, chain1, t) - go insertChain(done, blockchain, chain2, t) - - for i := 0; i < max; i++ { - <-done - } - - if chain2[len(chain2)-1].Hash() != blockchain.CurrentBlock().Hash() { - t.Error("chain2 is canonical and shouldn't be") - } - - if chain1[len(chain1)-1].Hash() != blockchain.CurrentBlock().Hash() { - t.Error("chain1 isn't canonical and should be") - } -} - -func TestChainMultipleInsertions(t *testing.T) { - t.Skip("Skipped: outdated test files") - - db, _ := ethdb.NewMemDatabase() - - const max = 4 - chains := make([]types.Blocks, max) - var longest int - for i := 0; i < max; i++ { - var err error - name := "valid" + strconv.Itoa(i+1) - chains[i], err = loadChain(name, t) - if len(chains[i]) >= len(chains[longest]) { - longest = i - } - fmt.Println("loaded", name, "with a length of", len(chains[i])) - if err != nil { - fmt.Println(err) - t.FailNow() - } - } - - blockchain := theBlockChain(db, t) - - done := make(chan bool, max) - for i, chain := range chains { - // XXX the go routine would otherwise reference the same (chain[3]) variable and fail - i := i - chain := chain - go func() { - insertChain(done, blockchain, chain, t) - fmt.Println(i, "done") - }() - } - - for i := 0; i < max; i++ { - <-done - } - - if chains[longest][len(chains[longest])-1].Hash() != blockchain.CurrentBlock().Hash() { - t.Error("Invalid canonical chain") - } -} - type bproc struct{} -func (bproc) ValidateBlock(*types.Block) error { return nil } -func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil } +func (bproc) ValidateBody(*types.Block) error { return nil } func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error { return nil } @@ -452,6 +352,7 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B UncleHash: types.EmptyUncleHash, TxHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash, + Time: big.NewInt(int64(i) + 1), } if i == 0 { header.ParentHash = genesis.Hash() @@ -464,29 +365,6 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B return chain } -func chm(genesis *types.Block, db ethdb.Database) *BlockChain { - var eventMux event.TypeMux - bc := &BlockChain{ - chainDb: db, - genesisBlock: genesis, - eventMux: &eventMux, - pow: pow.FakePow{}, - config: testChainConfig(), - } - valFn := func() HeaderValidator { return bc.Validator() } - bc.hc, _ = NewHeaderChain(db, testChainConfig(), valFn, bc.getProcInterrupt) - bc.bodyCache, _ = lru.New(100) - bc.bodyRLPCache, _ = lru.New(100) - bc.blockCache, _ = lru.New(100) - bc.futureBlocks, _ = lru.New(100) - bc.badBlocks, _ = lru.New(10) - bc.SetValidator(bproc{}) - bc.SetProcessor(bproc{}) - bc.ResetWithGenesisBlock(genesis) - - return bc -} - // Tests that reorganising a long difficult chain after a short easy one // overwrites the canonical numbers and links in the database. func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) } @@ -506,18 +384,15 @@ func testReorgShort(t *testing.T, full bool) { } func testReorg(t *testing.T, first, second []int, td int64, full bool) { - // Create a pristine block chain - db, _ := ethdb.NewMemDatabase() - genesis, _ := WriteTestNetGenesisBlock(db) - bc := chm(genesis, db) + bc := newTestBlockChain(true) // Insert an easy and a difficult chain afterwards if full { - bc.InsertChain(makeBlockChainWithDiff(genesis, first, 11)) - bc.InsertChain(makeBlockChainWithDiff(genesis, second, 22)) + bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, first, 11)) + bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, second, 22)) } else { - bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, first, 11), 1) - bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, second, 22), 1) + bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1) + bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1) } // Check that the chain is valid number and link wise if full { @@ -536,7 +411,7 @@ func testReorg(t *testing.T, first, second []int, td int64, full bool) { } } // Make sure the chain total difficulty is the correct one - want := new(big.Int).Add(genesis.Difficulty(), big.NewInt(td)) + want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td)) if full { if have := bc.GetTdByHash(bc.CurrentBlock().Hash()); have.Cmp(want) != 0 { t.Errorf("total difficulty mismatch: have %v, want %v", have, want) @@ -553,24 +428,21 @@ func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) } func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) } func testBadHashes(t *testing.T, full bool) { - // Create a pristine block chain - db, _ := ethdb.NewMemDatabase() - genesis, _ := WriteTestNetGenesisBlock(db) - bc := chm(genesis, db) + bc := newTestBlockChain(true) // Create a chain, ban a hash and try to import var err error if full { - blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 4}, 10) + blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10) BadHashes[blocks[2].Header().Hash()] = true _, err = bc.InsertChain(blocks) } else { - headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 4}, 10) + headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10) BadHashes[headers[2].Hash()] = true _, err = bc.InsertHeaderChain(headers, 1) } - if !IsBadHashError(err) { - t.Errorf("error mismatch: want: BadHashError, have: %v", err) + if err != ErrBlacklistedHash { + t.Errorf("error mismatch: have: %v, want: %v", err, ErrBlacklistedHash) } } @@ -580,14 +452,11 @@ func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) } func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) } func testReorgBadHashes(t *testing.T, full bool) { - // Create a pristine block chain - db, _ := ethdb.NewMemDatabase() - genesis, _ := WriteTestNetGenesisBlock(db) - bc := chm(genesis, db) + bc := newTestBlockChain(true) // Create a chain, import and ban afterwards - headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) - blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) + headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10) + blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10) if full { if _, err := bc.InsertChain(blocks); err != nil { @@ -608,8 +477,9 @@ func testReorgBadHashes(t *testing.T, full bool) { BadHashes[headers[3].Hash()] = true defer func() { delete(BadHashes, headers[3].Hash()) }() } - // Create a new chain manager and check it rolled back the state - ncm, err := NewBlockChain(db, testChainConfig(), pow.FakePow{}, new(event.TypeMux), vm.Config{}) + + // Create a new BlockChain and check that it rolled back the state. + ncm, err := NewBlockChain(bc.chainDb, bc.config, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) if err != nil { t.Fatalf("failed to create new chain manager: %v", err) } @@ -640,47 +510,32 @@ func testInsertNonceError(t *testing.T, full bool) { } // Create and insert a chain with a failing nonce var ( - failAt int - failRes int - failNum uint64 - failHash common.Hash + failAt int + failRes int + failNum uint64 ) if full { blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0) failAt = rand.Int() % len(blocks) failNum = blocks[failAt].NumberU64() - failHash = blocks[failAt].Hash() - - blockchain.pow = failPow{failNum} + blockchain.engine = ethash.NewFakeFailer(failNum) failRes, err = blockchain.InsertChain(blocks) } else { headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0) failAt = rand.Int() % len(headers) failNum = headers[failAt].Number.Uint64() - failHash = headers[failAt].Hash() - - blockchain.pow = failPow{failNum} - blockchain.validator = NewBlockValidator(testChainConfig(), blockchain, failPow{failNum}) + blockchain.engine = ethash.NewFakeFailer(failNum) + blockchain.hc.engine = blockchain.engine failRes, err = blockchain.InsertHeaderChain(headers, 1) } - // Check that the returned error indicates the nonce failure. + // Check that the returned error indicates the failure. if failRes != failAt { t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt) } - if !IsBlockNonceErr(err) { - t.Fatalf("test %d: error mismatch: have %v, want nonce error %T", i, err, err) - } - nerr := err.(*BlockNonceErr) - if nerr.Number.Uint64() != failNum { - t.Errorf("test %d: number mismatch: have %v, want %v", i, nerr.Number, failNum) - } - if nerr.Hash != failHash { - t.Errorf("test %d: hash mismatch: have %x, want %x", i, nerr.Hash[:4], failHash[:4]) - } // Check that all no blocks after the failing block have been inserted. for j := 0; j < i-failAt; j++ { if full { @@ -705,10 +560,14 @@ func TestFastVsFullChains(t *testing.T) { key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) - genesis = GenesisBlockForTesting(gendb, address, funds) - signer = types.NewEIP155Signer(big.NewInt(1)) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{address: {Balance: funds}}, + } + genesis = gspec.MustCommit(gendb) + signer = types.NewEIP155Signer(gspec.Config.ChainId) ) - blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, 1024, func(i int, block *BlockGen) { + blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, 1024, func(i int, block *BlockGen) { block.SetCoinbase(common.Address{0x00}) // If the block number is multiple of 3, send a few bonus transactions to the miner @@ -728,17 +587,17 @@ func TestFastVsFullChains(t *testing.T) { }) // Import the chain as an archive node for the comparison baseline archiveDb, _ := ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) - - archive, _ := NewBlockChain(archiveDb, testChainConfig(), pow.FakePow{}, new(event.TypeMux), vm.Config{}) + gspec.MustCommit(archiveDb) + archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) if n, err := archive.InsertChain(blocks); err != nil { t.Fatalf("failed to process block %d: %v", n, err) } + // Fast import the chain as a non-archive node to test fastDb, _ := ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) - fast, _ := NewBlockChain(fastDb, testChainConfig(), pow.FakePow{}, new(event.TypeMux), vm.Config{}) + gspec.MustCommit(fastDb) + fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -788,10 +647,11 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) - genesis = GenesisBlockForTesting(gendb, address, funds) + gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}} + genesis = gspec.MustCommit(gendb) ) height := uint64(1024) - blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, int(height), nil) + blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, int(height), nil) // Configure a subchain to roll back remove := []common.Hash{} @@ -812,10 +672,9 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { } // Import the chain as an archive node and ensure all pointers are updated archiveDb, _ := ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) - - archive, _ := NewBlockChain(archiveDb, testChainConfig(), pow.FakePow{}, new(event.TypeMux), vm.Config{}) + gspec.MustCommit(archiveDb) + archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) if n, err := archive.InsertChain(blocks); err != nil { t.Fatalf("failed to process block %d: %v", n, err) } @@ -825,8 +684,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { // Import the chain as a non-archive node and ensure all pointers are updated fastDb, _ := ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) - fast, _ := NewBlockChain(fastDb, testChainConfig(), pow.FakePow{}, new(event.TypeMux), vm.Config{}) + gspec.MustCommit(fastDb) + fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -844,9 +703,9 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { // Import the chain as a light node and ensure all pointers are updated lightDb, _ := ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds}) - light, _ := NewBlockChain(lightDb, testChainConfig(), pow.FakePow{}, new(event.TypeMux), vm.Config{}) + gspec.MustCommit(lightDb) + light, _ := NewBlockChain(lightDb, gspec.Config, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) if n, err := light.InsertHeaderChain(headers, 1); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) } @@ -857,9 +716,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { // Tests that chain reorganisations handle transaction removals and reinsertions. func TestChainTxReorgs(t *testing.T) { - params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be. - params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block. - var ( key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") @@ -868,13 +724,19 @@ func TestChainTxReorgs(t *testing.T) { addr2 = crypto.PubkeyToAddress(key2.PublicKey) addr3 = crypto.PubkeyToAddress(key3.PublicKey) db, _ = ethdb.NewMemDatabase() - signer = types.NewEIP155Signer(big.NewInt(1)) - ) - genesis := WriteGenesisBlockForTesting(db, - GenesisAccount{addr1, big.NewInt(1000000)}, - GenesisAccount{addr2, big.NewInt(1000000)}, - GenesisAccount{addr3, big.NewInt(1000000)}, + gspec = &Genesis{ + Config: params.TestChainConfig, + GasLimit: 3141592, + Alloc: GenesisAlloc{ + addr1: {Balance: big.NewInt(1000000)}, + addr2: {Balance: big.NewInt(1000000)}, + addr3: {Balance: big.NewInt(1000000)}, + }, + } + genesis = gspec.MustCommit(db) + signer = types.NewEIP155Signer(gspec.Config.ChainId) ) + // Create two transactions shared between the chains: // - postponed: transaction included at a later block in the forked chain // - swapped: transaction included at the same block number in the forked chain @@ -892,7 +754,7 @@ func TestChainTxReorgs(t *testing.T) { // - futureAdd: transaction added after the reorg has already finished var pastAdd, freshAdd, futureAdd *types.Transaction - chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) { switch i { case 0: pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2) @@ -911,13 +773,13 @@ func TestChainTxReorgs(t *testing.T) { }) // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, testChainConfig(), pow.FakePow{}, evmux, vm.Config{}) + blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), evmux, vm.Config{}) if i, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert original chain[%d]: %v", i, err) } // overwrite the old chain - chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) { + chain, _ = GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) { switch i { case 0: pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3) @@ -969,23 +831,20 @@ func TestChainTxReorgs(t *testing.T) { } func TestLogReorgs(t *testing.T) { - params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be. - params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block. var ( key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) db, _ = ethdb.NewMemDatabase() // this code generates a log - code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") - signer = types.NewEIP155Signer(big.NewInt(1)) - ) - genesis := WriteGenesisBlockForTesting(db, - GenesisAccount{addr1, big.NewInt(10000000000000)}, + code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") + gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} + genesis = gspec.MustCommit(db) + signer = types.NewEIP155Signer(gspec.Config.ChainId) ) - evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, testChainConfig(), pow.FakePow{}, evmux, vm.Config{}) + var evmux event.TypeMux + blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), &evmux, vm.Config{}) subs := evmux.Subscribe(RemovedLogsEvent{}) chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) { @@ -1017,19 +876,23 @@ func TestReorgSideEvent(t *testing.T) { db, _ = ethdb.NewMemDatabase() key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") addr1 = crypto.PubkeyToAddress(key1.PublicKey) - genesis = WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(10000000000000)}) - signer = types.NewEIP155Signer(big.NewInt(1)) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}, + } + genesis = gspec.MustCommit(db) + signer = types.NewEIP155Signer(gspec.Config.ChainId) ) evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, testChainConfig(), pow.FakePow{}, evmux, vm.Config{}) + blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), evmux, vm.Config{}) - chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {}) + chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) {}) if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert chain: %v", err) } - replacementBlocks, _ := GenerateChain(params.TestChainConfig, genesis, db, 4, func(i int, gen *BlockGen) { + replacementBlocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, gen *BlockGen) { tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil), signer, key1) if i == 2 { gen.OffsetTime(-1) @@ -1092,28 +955,21 @@ done: // Tests if the canonical block can be fetched from the database during chain insertion. func TestCanonicalBlockRetrieval(t *testing.T) { - var ( - db, _ = ethdb.NewMemDatabase() - genesis = WriteGenesisBlockForTesting(db) - ) - - evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, testChainConfig(), pow.FakePow{}, evmux, vm.Config{}) - - chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *BlockGen) {}) + bc := newTestBlockChain(false) + chain, _ := GenerateChain(bc.config, bc.genesisBlock, bc.chainDb, 10, func(i int, gen *BlockGen) {}) for i := range chain { go func(block *types.Block) { // try to retrieve a block by its canonical hash and see if the block data can be retrieved. for { - ch := GetCanonicalHash(db, block.NumberU64()) + ch := GetCanonicalHash(bc.chainDb, block.NumberU64()) if ch == (common.Hash{}) { continue // busy wait for canonical hash to be written } if ch != block.Hash() { t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex()) } - fb := GetBlock(db, ch, block.NumberU64()) + fb := GetBlock(bc.chainDb, ch, block.NumberU64()) if fb == nil { t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex()) } @@ -1124,7 +980,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) { } }(chain[i]) - blockchain.InsertChain(types.Blocks{chain[i]}) + bc.InsertChain(types.Blocks{chain[i]}) } } @@ -1136,13 +992,16 @@ func TestEIP155Transition(t *testing.T) { address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) deleteAddr = common.Address{1} - genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}, GenesisAccount{deleteAddr, new(big.Int)}) - config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} - mux event.TypeMux + gspec = &Genesis{ + Config: ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}, + Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}}, + } + genesis = gspec.MustCommit(db) + mux event.TypeMux ) - blockchain, _ := NewBlockChain(db, config, pow.FakePow{}, &mux, vm.Config{}) - blocks, _ := GenerateChain(config, genesis, db, 4, func(i int, block *BlockGen) { + blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), &mux, vm.Config{}) + blocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, block *BlockGen) { var ( tx *types.Transaction err error @@ -1164,7 +1023,7 @@ func TestEIP155Transition(t *testing.T) { } block.AddTx(tx) - tx, err = basicTx(types.NewEIP155Signer(config.ChainId)) + tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId)) if err != nil { t.Fatal(err) } @@ -1176,7 +1035,7 @@ func TestEIP155Transition(t *testing.T) { } block.AddTx(tx) - tx, err = basicTx(types.NewEIP155Signer(config.ChainId)) + tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId)) if err != nil { t.Fatal(err) } @@ -1204,7 +1063,7 @@ func TestEIP155Transition(t *testing.T) { } // generate an invalid chain id transaction - config = ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} + config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) { var ( tx *types.Transaction @@ -1236,22 +1095,24 @@ func TestEIP161AccountRemoval(t *testing.T) { address = crypto.PubkeyToAddress(key.PublicKey) funds = big.NewInt(1000000000) theAddr = common.Address{1} - genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}) - config = ¶ms.ChainConfig{ - ChainId: big.NewInt(1), - HomesteadBlock: new(big.Int), - EIP155Block: new(big.Int), - EIP158Block: big.NewInt(2), - } - mux event.TypeMux - - blockchain, _ = NewBlockChain(db, config, pow.FakePow{}, &mux, vm.Config{}) + gspec = &Genesis{ + Config: ¶ms.ChainConfig{ + ChainId: big.NewInt(1), + HomesteadBlock: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: big.NewInt(2), + }, + Alloc: GenesisAlloc{address: {Balance: funds}}, + } + genesis = gspec.MustCommit(db) + mux event.TypeMux + blockchain, _ = NewBlockChain(db, gspec.Config, ethash.NewFaker(), &mux, vm.Config{}) ) - blocks, _ := GenerateChain(config, genesis, db, 3, func(i int, block *BlockGen) { + blocks, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, block *BlockGen) { var ( tx *types.Transaction err error - signer = types.NewEIP155Signer(config.ChainId) + signer = types.NewEIP155Signer(gspec.Config.ChainId) ) switch i { case 0: @@ -1279,7 +1140,7 @@ func TestEIP161AccountRemoval(t *testing.T) { t.Fatal(err) } if blockchain.stateCache.Exist(theAddr) { - t.Error("account should not expect") + t.Error("account should not exist") } // account musn't be created post eip 161 @@ -1287,6 +1148,6 @@ func TestEIP161AccountRemoval(t *testing.T) { t.Fatal(err) } if blockchain.stateCache.Exist(theAddr) { - t.Error("account should not expect") + t.Error("account should not exist") } } diff --git a/core/chain_makers.go b/core/chain_makers.go index f63496fdc..f34279ba0 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -21,28 +21,16 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) -/* - * TODO: move this to another package. - */ - -// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings. -func MakeChainConfig() *params.ChainConfig { - return ¶ms.ChainConfig{ - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - } -} - // So we can deterministically seed different blockchains var ( canonicalSeed = 1 @@ -97,7 +85,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) + receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } @@ -154,7 +142,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { panic("block time out of range") } - b.header.Difficulty = CalcDifficulty(MakeChainConfig(), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) + b.header.Difficulty = ethash.CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) } // GenerateChain creates a chain of n blocks. The first block's @@ -170,14 +158,13 @@ func (b *BlockGen) OffsetTime(seconds int64) { // values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { + if config == nil { + config = params.TestChainConfig + } blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) { b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config} - // Mutate the state and block according to any hard-fork specs - if config == nil { - config = MakeChainConfig() - } if daoBlock := config.DAOForkBlock; daoBlock != nil { limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 { @@ -187,13 +174,13 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Dat } } if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 { - ApplyDAOHardFork(statedb) + misc.ApplyDAOHardFork(statedb) } // Execute any user modifications to the block and finalize it if gen != nil { gen(i, b) } - AccumulateRewards(statedb, h, b.uncles) + ethash.AccumulateRewards(statedb, h, b.uncles) root, err := statedb.Commit(config.IsEIP158(h.Number)) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) @@ -226,7 +213,7 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), - Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), + Difficulty: ethash.CalcDifficulty(config, time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), GasLimit: CalcGasLimit(parent), GasUsed: new(big.Int), Number: new(big.Int).Add(parent.Number(), common.Big1), @@ -238,14 +225,12 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St // chain. Depending on the full flag, if creates either a full block chain or a // header only chain. func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) { - // Create the new chain database - db, _ := ethdb.NewMemDatabase() - evmux := &event.TypeMux{} - // Initialize a fresh chain with only a genesis block - genesis, _ := WriteTestNetGenesisBlock(db) + gspec := new(Genesis) + db, _ := ethdb.NewMemDatabase() + genesis := gspec.MustCommit(db) - blockchain, _ := NewBlockChain(db, MakeChainConfig(), pow.FakePow{}, evmux, vm.Config{}) + blockchain, _ := NewBlockChain(db, params.AllProtocolChanges, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) // Create and inject the requested chain if n == 0 { return db, blockchain, nil diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 82751553f..3a7c62396 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -20,19 +20,16 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) func ExampleGenerateChain() { - params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be. - params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block. - var ( key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") @@ -41,19 +38,20 @@ func ExampleGenerateChain() { addr2 = crypto.PubkeyToAddress(key2.PublicKey) addr3 = crypto.PubkeyToAddress(key3.PublicKey) db, _ = ethdb.NewMemDatabase() - signer = types.HomesteadSigner{} ) - chainConfig := ¶ms.ChainConfig{ - HomesteadBlock: new(big.Int), - } // Ensure that key1 has some funds in the genesis block. - genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(1000000)}) + gspec := &Genesis{ + Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, + Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, + } + genesis := gspec.MustCommit(db) // This call generates a chain of 5 blocks. The function runs for // each block and adds different features to gen based on the // block index. - chain, _ := GenerateChain(chainConfig, genesis, db, 5, func(i int, gen *BlockGen) { + signer := types.HomesteadSigner{} + chain, _ := GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) { switch i { case 0: // In block 1, addr1 sends addr2 some ether. @@ -83,7 +81,7 @@ func ExampleGenerateChain() { // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, chainConfig, pow.FakePow{}, evmux, vm.Config{}) + blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), evmux, vm.Config{}) if i, err := blockchain.InsertChain(chain); err != nil { fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err) return diff --git a/core/chain_pow.go b/core/chain_pow.go deleted file mode 100644 index e5ccd87e2..000000000 --- a/core/chain_pow.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package core - -import ( - "runtime" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/pow" -) - -// nonceCheckResult contains the result of a nonce verification. -type nonceCheckResult struct { - index int // Index of the item verified from an input array - valid bool // Result of the nonce verification -} - -// verifyNoncesFromHeaders starts a concurrent header nonce verification, -// returning a quit channel to abort the operations and a results channel -// to retrieve the async verifications. -func verifyNoncesFromHeaders(checker pow.PoW, headers []*types.Header) (chan<- struct{}, <-chan nonceCheckResult) { - items := make([]pow.Block, len(headers)) - for i, header := range headers { - items[i] = types.NewBlockWithHeader(header) - } - return verifyNonces(checker, items) -} - -// verifyNoncesFromBlocks starts a concurrent block nonce verification, -// returning a quit channel to abort the operations and a results channel -// to retrieve the async verifications. -func verifyNoncesFromBlocks(checker pow.PoW, blocks []*types.Block) (chan<- struct{}, <-chan nonceCheckResult) { - items := make([]pow.Block, len(blocks)) - for i, block := range blocks { - items[i] = block - } - return verifyNonces(checker, items) -} - -// verifyNonces starts a concurrent nonce verification, returning a quit channel -// to abort the operations and a results channel to retrieve the async checks. -func verifyNonces(checker pow.PoW, items []pow.Block) (chan<- struct{}, <-chan nonceCheckResult) { - // Spawn as many workers as allowed threads - workers := runtime.GOMAXPROCS(0) - if len(items) < workers { - workers = len(items) - } - // Create a task channel and spawn the verifiers - tasks := make(chan int, workers) - results := make(chan nonceCheckResult, len(items)) // Buffered to make sure all workers stop - for i := 0; i < workers; i++ { - go func() { - for index := range tasks { - results <- nonceCheckResult{index: index, valid: checker.Verify(items[index]) == nil} - } - }() - } - // Feed item indices to the workers until done or aborted - abort := make(chan struct{}) - go func() { - defer close(tasks) - - for i := range items { - select { - case tasks <- i: - continue - case <-abort: - return - } - } - }() - return abort, results -} diff --git a/core/chain_pow_test.go b/core/chain_pow_test.go deleted file mode 100644 index 311ca128e..000000000 --- a/core/chain_pow_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package core - -import ( - "errors" - "math/big" - "runtime" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" -) - -// failPow is a non-validating proof of work implementation, that returns true -// from Verify for all but one block. -type failPow struct { - failing uint64 -} - -func (pow failPow) Search(pow.Block, <-chan struct{}) (uint64, []byte) { - return 0, nil -} -func (pow failPow) Verify(block pow.Block) error { - if block.NumberU64() == pow.failing { - return errors.New("failed") - } - return nil -} -func (pow failPow) Hashrate() float64 { return 0 } - -// delayedPow is a non-validating proof of work implementation, that returns true -// from Verify for all blocks, but delays them the configured amount of time. -type delayedPow struct { - delay time.Duration -} - -func (pow delayedPow) Search(pow.Block, <-chan struct{}) (uint64, []byte) { - return 0, nil -} -func (pow delayedPow) Verify(block pow.Block) error { time.Sleep(pow.delay); return nil } -func (pow delayedPow) Hashrate() float64 { return 0 } - -// Tests that simple POW verification works, for both good and bad blocks. -func TestPowVerification(t *testing.T) { - // Create a simple chain to verify - var ( - testdb, _ = ethdb.NewMemDatabase() - genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) - blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) - ) - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - // Run the POW checker for blocks one-by-one, checking for both valid and invalid nonces - for i := 0; i < len(blocks); i++ { - for j, full := range []bool{true, false} { - for k, valid := range []bool{true, false} { - var results <-chan nonceCheckResult - - switch { - case full && valid: - _, results = verifyNoncesFromBlocks(pow.FakePow{}, []*types.Block{blocks[i]}) - case full && !valid: - _, results = verifyNoncesFromBlocks(failPow{blocks[i].NumberU64()}, []*types.Block{blocks[i]}) - case !full && valid: - _, results = verifyNoncesFromHeaders(pow.FakePow{}, []*types.Header{headers[i]}) - case !full && !valid: - _, results = verifyNoncesFromHeaders(failPow{headers[i].Number.Uint64()}, []*types.Header{headers[i]}) - } - // Wait for the verification result - select { - case result := <-results: - if result.index != 0 { - t.Errorf("test %d.%d.%d: invalid index: have %d, want 0", i, j, k, result.index) - } - if result.valid != valid { - t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, result.valid, valid) - } - case <-time.After(time.Second): - t.Fatalf("test %d.%d.%d: verification timeout", i, j, k) - } - // Make sure no more data is returned - select { - case result := <-results: - t.Fatalf("test %d.%d.%d: unexpected result returned: %v", i, j, k, result) - case <-time.After(25 * time.Millisecond): - } - } - } - } -} - -// Tests that concurrent POW verification works, for both good and bad blocks. -func TestPowConcurrentVerification2(t *testing.T) { testPowConcurrentVerification(t, 2) } -func TestPowConcurrentVerification8(t *testing.T) { testPowConcurrentVerification(t, 8) } -func TestPowConcurrentVerification32(t *testing.T) { testPowConcurrentVerification(t, 32) } - -func testPowConcurrentVerification(t *testing.T, threads int) { - // Create a simple chain to verify - var ( - testdb, _ = ethdb.NewMemDatabase() - genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) - blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) - ) - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - // Set the number of threads to verify on - old := runtime.GOMAXPROCS(threads) - defer runtime.GOMAXPROCS(old) - - // Run the POW checker for the entire block chain at once both for a valid and - // also an invalid chain (enough if one is invalid, last but one (arbitrary)). - for i, full := range []bool{true, false} { - for j, valid := range []bool{true, false} { - var results <-chan nonceCheckResult - - switch { - case full && valid: - _, results = verifyNoncesFromBlocks(pow.FakePow{}, blocks) - case full && !valid: - _, results = verifyNoncesFromBlocks(failPow{uint64(len(blocks) - 1)}, blocks) - case !full && valid: - _, results = verifyNoncesFromHeaders(pow.FakePow{}, headers) - case !full && !valid: - _, results = verifyNoncesFromHeaders(failPow{uint64(len(headers) - 1)}, headers) - } - // Wait for all the verification results - checks := make(map[int]bool) - for k := 0; k < len(blocks); k++ { - select { - case result := <-results: - if _, ok := checks[result.index]; ok { - t.Fatalf("test %d.%d.%d: duplicate results for %d", i, j, k, result.index) - } - if result.index < 0 || result.index >= len(blocks) { - t.Fatalf("test %d.%d.%d: result %d out of bounds [%d, %d]", i, j, k, result.index, 0, len(blocks)-1) - } - checks[result.index] = result.valid - - case <-time.After(time.Second): - t.Fatalf("test %d.%d.%d: verification timeout", i, j, k) - } - } - // Check nonce check validity - for k := 0; k < len(blocks); k++ { - want := valid || (k != len(blocks)-2) // We chose the last but one nonce in the chain to fail - if checks[k] != want { - t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, checks[k], want) - } - } - // Make sure no more data is returned - select { - case result := <-results: - t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result) - case <-time.After(25 * time.Millisecond): - } - } - } -} - -// Tests that aborting a POW validation indeed prevents further checks from being -// run, as well as checks that no left-over goroutines are leaked. -func TestPowConcurrentAbortion2(t *testing.T) { testPowConcurrentAbortion(t, 2) } -func TestPowConcurrentAbortion8(t *testing.T) { testPowConcurrentAbortion(t, 8) } -func TestPowConcurrentAbortion32(t *testing.T) { testPowConcurrentAbortion(t, 32) } - -func testPowConcurrentAbortion(t *testing.T, threads int) { - // Create a simple chain to verify - var ( - testdb, _ = ethdb.NewMemDatabase() - genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) - blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil) - ) - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - // Set the number of threads to verify on - old := runtime.GOMAXPROCS(threads) - defer runtime.GOMAXPROCS(old) - - // Run the POW checker for the entire block chain at once - for i, full := range []bool{true, false} { - var abort chan<- struct{} - var results <-chan nonceCheckResult - - // Start the verifications and immediately abort - if full { - abort, results = verifyNoncesFromBlocks(delayedPow{time.Millisecond}, blocks) - } else { - abort, results = verifyNoncesFromHeaders(delayedPow{time.Millisecond}, headers) - } - close(abort) - - // Deplete the results channel - verified := make(map[int]struct{}) - for depleted := false; !depleted; { - select { - case result := <-results: - verified[result.index] = struct{}{} - case <-time.After(50 * time.Millisecond): - depleted = true - } - } - // Check that abortion was honored by not processing too many POWs - if len(verified) > 2*threads { - t.Errorf("test %d: verification count too large: have %d, want below %d", i, len(verified), 2*threads) - } - // Check that there are no gaps in the results - for j := 0; j < len(verified); j++ { - if _, ok := verified[j]; !ok { - t.Errorf("test %d.%d: gap found in verification results", i, j) - } - } - } -} diff --git a/core/dao.go b/core/dao.go index 7e376e68a..ff42a0e9d 100644 --- a/core/dao.go +++ b/core/dao.go @@ -18,6 +18,7 @@ package core import ( "bytes" + "fmt" "math/big" "github.com/ethereum/go-ethereum/core/state" @@ -46,11 +47,11 @@ func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header // Depending whether we support or oppose the fork, validate the extra-data contents if config.DAOForkSupport { if !bytes.Equal(header.Extra, params.DAOForkBlockExtra) { - return ValidationError("DAO pro-fork bad block extra-data: 0x%x", header.Extra) + return fmt.Errorf("DAO pro-fork bad block extra-data: 0x%x", header.Extra) } } else { if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { - return ValidationError("DAO no-fork bad block extra-data: 0x%x", header.Extra) + return fmt.Errorf("DAO no-fork bad block extra-data: 0x%x", header.Extra) } } // All ok, header has the same extra-data we expect @@ -67,7 +68,7 @@ func ApplyDAOHardFork(statedb *state.StateDB) { } // Move every DAO account and extra-balance account funds into the refund contract - for _, addr := range params.DAODrainList { + for _, addr := range params.DAODrainList() { statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr)) statedb.SetBalance(addr, new(big.Int)) } diff --git a/core/dao_test.go b/core/dao_test.go index 45b3235c7..bc9f3f394 100644 --- a/core/dao_test.go +++ b/core/dao_test.go @@ -20,11 +20,11 @@ import ( "math/big" "testing" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) // Tests that DAO-fork enabled clients can properly filter out fork-commencing @@ -34,19 +34,20 @@ func TestDAOForkRangeExtradata(t *testing.T) { // Generate a common prefix for both pro-forkers and non-forkers db, _ := ethdb.NewMemDatabase() - genesis := WriteGenesisBlockForTesting(db) + gspec := new(Genesis) + genesis := gspec.MustCommit(db) prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {}) // Create the concurrent, conflicting two nodes proDb, _ := ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(proDb) + gspec.MustCommit(proDb) proConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true} - proBc, _ := NewBlockChain(proDb, proConf, new(pow.FakePow), new(event.TypeMux), vm.Config{}) + proBc, _ := NewBlockChain(proDb, proConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) conDb, _ := ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(conDb) + gspec.MustCommit(conDb) conConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false} - conBc, _ := NewBlockChain(conDb, conConf, new(pow.FakePow), new(event.TypeMux), vm.Config{}) + conBc, _ := NewBlockChain(conDb, conConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) if _, err := proBc.InsertChain(prefix); err != nil { t.Fatalf("pro-fork: failed to import chain prefix: %v", err) @@ -58,10 +59,10 @@ func TestDAOForkRangeExtradata(t *testing.T) { for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ { // Create a pro-fork block, and try to feed into the no-fork chain db, _ = ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(db) - bc, _ := NewBlockChain(db, conConf, new(pow.FakePow), new(event.TypeMux), vm.Config{}) + gspec.MustCommit(db) + bc, _ := NewBlockChain(db, conConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) - blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1)) + blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) for j := 0; j < len(blocks)/2; j++ { blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] } @@ -79,10 +80,10 @@ func TestDAOForkRangeExtradata(t *testing.T) { } // Create a no-fork block, and try to feed into the pro-fork chain db, _ = ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(db) - bc, _ = NewBlockChain(db, proConf, new(pow.FakePow), new(event.TypeMux), vm.Config{}) + gspec.MustCommit(db) + bc, _ = NewBlockChain(db, proConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) - blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1)) + blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) for j := 0; j < len(blocks)/2; j++ { blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] } @@ -101,10 +102,10 @@ func TestDAOForkRangeExtradata(t *testing.T) { } // Verify that contra-forkers accept pro-fork extra-datas after forking finishes db, _ = ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(db) - bc, _ := NewBlockChain(db, conConf, new(pow.FakePow), new(event.TypeMux), vm.Config{}) + gspec.MustCommit(db) + bc, _ := NewBlockChain(db, conConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) - blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1)) + blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) for j := 0; j < len(blocks)/2; j++ { blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] } @@ -117,10 +118,10 @@ func TestDAOForkRangeExtradata(t *testing.T) { } // Verify that pro-forkers accept contra-fork extra-datas after forking finishes db, _ = ethdb.NewMemDatabase() - WriteGenesisBlockForTesting(db) - bc, _ = NewBlockChain(db, proConf, new(pow.FakePow), new(event.TypeMux), vm.Config{}) + gspec.MustCommit(db) + bc, _ = NewBlockChain(db, proConf, ethash.NewFaker(), new(event.TypeMux), vm.Config{}) - blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1)) + blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) for j := 0; j < len(blocks)/2; j++ { blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] } diff --git a/core/database_util_test.go b/core/database_util_test.go index 0af4e4920..9f16b660a 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -18,14 +18,12 @@ package core import ( "bytes" - "encoding/json" "io/ioutil" "math/big" "os" "testing" "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/crypto" "github.com/ethereum/go-ethereum/crypto/sha3" @@ -34,58 +32,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -type diffTest struct { - ParentTimestamp uint64 - ParentDifficulty *big.Int - CurrentTimestamp uint64 - CurrentBlocknumber *big.Int - CurrentDifficulty *big.Int -} - -func (d *diffTest) UnmarshalJSON(b []byte) (err error) { - var ext struct { - ParentTimestamp string - ParentDifficulty string - CurrentTimestamp string - CurrentBlocknumber string - CurrentDifficulty string - } - if err := json.Unmarshal(b, &ext); err != nil { - return err - } - - d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp) - d.ParentDifficulty = math.MustParseBig256(ext.ParentDifficulty) - d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp) - d.CurrentBlocknumber = math.MustParseBig256(ext.CurrentBlocknumber) - d.CurrentDifficulty = math.MustParseBig256(ext.CurrentDifficulty) - - return nil -} - -func TestCalcDifficulty(t *testing.T) { - file, err := os.Open("../tests/files/BasicTests/difficulty.json") - if err != nil { - t.Fatal(err) - } - defer file.Close() - - tests := make(map[string]diffTest) - err = json.NewDecoder(file).Decode(&tests) - if err != nil { - t.Fatal(err) - } - - config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)} - for name, test := range tests { - number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1)) - diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty) - if diff.Cmp(test.CurrentDifficulty) != 0 { - t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff) - } - } -} - // Tests block header storage and retrieval operations. func TestHeaderStorage(t *testing.T) { db, _ := ethdb.NewMemDatabase() @@ -562,7 +508,11 @@ func TestMipmapChain(t *testing.T) { ) defer db.Close() - genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)}) + gspec := &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{addr: {Balance: big.NewInt(1000000)}}, + } + genesis := gspec.MustCommit(db) chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) { var receipts types.Receipts switch i { diff --git a/core/default_genesis.go b/core/default_genesis.go deleted file mode 100644 index 08fd432d7..000000000 --- a/core/default_genesis.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 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 <http://www.gnu.org/licenses/>. - -package core - -// defaultGenesisBlock is a gzip compressed dump of the official default Ethereum -// genesis block. -const defaultGenesisBlock = "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" - -// defaultTestnetGenesisBlock is a gzip compressed dump of the official default Ethereum -// test network genesis block (currently Ropsten). -const defaultTestnetGenesisBlock = "QlpoOTFBWSZTWezl5v8AGI59gHRQABBcBH/0FGQ/795qUAN+PcGHAoQMVIwm0zCSp+ihiAANB6gqp+mmp6eMmqo0AADRpoABgAGjTQBk0BoMgNAbSqNAyDQyaBoNAyAGBUkhNNNA0JGmgD0g0AekxJtVIgTtRupd52vqSSSVfG2ALQBhhdbCIgmAgUiIJnEQTviQF8kkN1IOKiRVVFVSVXHT1cHLHdiSQBlAAG2JtCtNgCS5IEB6dNtP782Z729Tbt3Z6+uFr7u3w79widyYSMAhtlL5XeGZWdwEBNdFm+Hh5gVRBoslgksAMkUKmYlSJRTxBsQAAKKlnd3WjPWrNd66NR7WAu6AQL40vqlny1We+p3l1QThy4gRd2lSVjEKISLDFrVJ1UtjPEPctVSZGjBTJWRzpdgRmCCprJWGe3DK9wvz4eGvdv1ZZcMvxGgdakeMAVEeFywVQWLWCrUWg60Y9ZLoAuZ0uSJJUELb8N2e7j05d3Tlz7vRwgk+LisedJALLVUgFjLGzfv5SEGPLlhhjpx2aaO8T87whcYDmgPLVDBI3RF1TLnzvymWtN732u93GiAGiBdm5s1q9Haru7+yCLJAk3wBDTtrGeMVh3iJmXHo+OBAsqoDOZwjKM3d3mZmcHdBJiSVouSKVkDg+L4vSzNRqs7u6NEaIiUKoYxERGfTWIhoiHwQFESGzta21qu1qtZ3cAXQshVAUybTLbWttWh3iHLoDJEgkACRVCzbeX774Z6558+mfDTdz058efTpDbXai+tddbQBbn/YA9fwxy8/Pq3HFW7deL8W447duuyI2RGqIAAA9SOqHeXzbRtGiXeHiYBMggAAIyQh/f5vW96Xvvu7b7777yMURRHFE6EkEnxRIiOlY2ijs7RV4gYIB0R4LmgB49xg0Oz0bVr2a95u4AZE6rTjl2w58Cntu1ZV6e3Lw3ne2RegHw7R58NdbtVlJEkuqRV2WKa/hpbPIx3bIyX43aNWmezrCBry2ePzkA8reSwB/xdyRThQkOzl5v8=" - -// defaultDevnetGenesisBlockis a gzip compressed dump of a dev Ethereum network genesis block. -const defaultDevnetGenesisBlock = "QlpoOTFBWSZTWb66siUAAmldgEERWAR/8AAEP6eOakACGu6ULVwyRNGKeBCbUaAGIJUDJpT2lP1BQAAANqp6jTUxMIxGmAJgFVTAp4mjSaoAxAB4cLqQ6aBgTRPcAECErBIZ9PyZJw5IYvQPWlcXDXnhaC9buNtJaAFApAgwkUIRBmBY6EhTY8/6ZXse6lLD1Eh3sfas7VHnxse2LZeXpME9fADujvYB4Wr9cgQA+Wq0gRnrfVmgVwIiIrgfON+c4EhrApvQT2LjUh6y3x41XXOxriA89W52MDId9R7vasE3pfWWATQF7nAEAWRb861EU6M9SmBTET1UUGuBmgxzewhmUe4Rj4jSvgRFRTYgI8WzzcQn3nNr6x3GW94Yp1xG4kOoC/MxDhbF8uMahuMTmKyEep0SwLaxquoT3F9X0NxmouMWhxTAlxHQoN872vbYJS/Wrls2LuptmbKr4R6BJ/oJfPNpq+H362F5ZyhNQBxXep4ZOr94ylFM6UfOeUq37kFeGzXOle3LKItkvJ70OKqEqzujic72rnpZHym6b3xSONoWjYzRzIUbOKSUgdVtEhtvUg24asA2cy1zFzsfnj8CXe0Bdi7kinChIX11ZEo=" diff --git a/core/error.go b/core/error.go index 0ba506f46..9ac4fff51 100644 --- a/core/error.go +++ b/core/error.go @@ -16,188 +16,16 @@ package core -import ( - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" -) +import "errors" var ( - BlockNumberErr = errors.New("block number invalid") - BlockFutureErr = errors.New("block time is in the future") - BlockTSTooBigErr = errors.New("block time too big") - BlockEqualTSErr = errors.New("block time stamp equal to previous") -) - -// Parent error. In case a parent is unknown this error will be thrown -// by the block manager -type ParentErr struct { - Message string -} - -func (err *ParentErr) Error() string { - return err.Message -} - -func ParentError(hash common.Hash) error { - return &ParentErr{Message: fmt.Sprintf("Block's parent unknown %x", hash)} -} - -func IsParentErr(err error) bool { - _, ok := err.(*ParentErr) - return ok -} - -type UncleErr struct { - Message string -} - -func (err *UncleErr) Error() string { - return err.Message -} - -func UncleError(format string, v ...interface{}) error { - return &UncleErr{Message: fmt.Sprintf(format, v...)} -} - -func IsUncleErr(err error) bool { - _, ok := err.(*UncleErr) - return ok -} - -// Block validation error. If any validation fails, this error will be thrown -type ValidationErr struct { - Message string -} - -func (err *ValidationErr) Error() string { - return err.Message -} - -func ValidationError(format string, v ...interface{}) *ValidationErr { - return &ValidationErr{Message: fmt.Sprintf(format, v...)} -} - -func IsValidationErr(err error) bool { - _, ok := err.(*ValidationErr) - return ok -} - -type NonceErr struct { - Message string - Is, Exp uint64 -} - -func (err *NonceErr) Error() string { - return err.Message -} - -func NonceError(is, exp uint64) *NonceErr { - return &NonceErr{Message: fmt.Sprintf("Transaction w/ invalid nonce. tx=%d state=%d)", is, exp), Is: is, Exp: exp} -} - -func IsNonceErr(err error) bool { - _, ok := err.(*NonceErr) - return ok -} + // ErrKnownBlock is returned when a block to import is already known locally. + ErrKnownBlock = errors.New("block already known") -// BlockNonceErr indicates that a block's nonce is invalid. -type BlockNonceErr struct { - Number *big.Int - Hash common.Hash - Nonce uint64 -} + // ErrGasLimitReached is returned by the gas pool if the amount of gas required + // by a transaction is higher than what's left in the block. + ErrGasLimitReached = errors.New("gas limit reached") -func (err *BlockNonceErr) Error() string { - return fmt.Sprintf("nonce for #%d [%x…] is invalid (got %d)", err.Number, err.Hash, err.Nonce) -} - -// IsBlockNonceErr returns true for invalid block nonce errors. -func IsBlockNonceErr(err error) bool { - _, ok := err.(*BlockNonceErr) - return ok -} - -type InvalidTxErr struct { - Message string -} - -func (err *InvalidTxErr) Error() string { - return err.Message -} - -func InvalidTxError(err error) *InvalidTxErr { - return &InvalidTxErr{fmt.Sprintf("%v", err)} -} - -func IsInvalidTxErr(err error) bool { - _, ok := err.(*InvalidTxErr) - return ok -} - -type TDError struct { - a, b *big.Int -} - -func (self *TDError) Error() string { - return fmt.Sprintf("incoming chain has a lower or equal TD (%v <= %v)", self.a, self.b) -} -func IsTDError(e error) bool { - _, ok := e.(*TDError) - return ok -} - -type KnownBlockError struct { - number *big.Int - hash common.Hash -} - -func (self *KnownBlockError) Error() string { - return fmt.Sprintf("block %v already known (%x)", self.number, self.hash[0:4]) -} -func IsKnownBlockErr(e error) bool { - _, ok := e.(*KnownBlockError) - return ok -} - -type ValueTransferError struct { - message string -} - -func ValueTransferErr(str string, v ...interface{}) *ValueTransferError { - return &ValueTransferError{fmt.Sprintf(str, v...)} -} - -func (self *ValueTransferError) Error() string { - return self.message -} -func IsValueTransferErr(e error) bool { - _, ok := e.(*ValueTransferError) - return ok -} - -type BadHashError common.Hash - -func (h BadHashError) Error() string { - return fmt.Sprintf("Found known bad hash in chain %x", h[:]) -} - -func IsBadHashError(err error) bool { - _, ok := err.(BadHashError) - return ok -} - -type GasLimitErr struct { - Have, Want *big.Int -} - -func IsGasLimitErr(err error) bool { - _, ok := err.(*GasLimitErr) - return ok -} - -func (err *GasLimitErr) Error() string { - return fmt.Sprintf("GasLimit reached. Have %d gas, transaction requires %d", err.Have, err.Want) -} + // ErrBlacklistedHash is returned if a block to import is on the blacklist. + ErrBlacklistedHash = errors.New("blacklisted hash") +) diff --git a/core/evm.go b/core/evm.go index 6a5713075..49a786a86 100644 --- a/core/evm.go +++ b/core/evm.go @@ -20,25 +20,36 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) -// BlockFetcher retrieves headers by their hash -type HeaderFetcher interface { - // GetHeader returns the hash corresponding to their hash +// ChainContext supports retrieving headers and consensus parameters from the +// current blockchain to be used during transaction processing. +type ChainContext interface { + // Engine retrieves the chain's consensus engine. + Engine() consensus.Engine + + // GetHeader returns the hash corresponding to their hash. GetHeader(common.Hash, uint64) *types.Header } // NewEVMContext creates a new context for use in the EVM. -func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context { +func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author *common.Address) vm.Context { + // If we don't have an explicit author (i.e. not mining), extract from the header + var beneficiary common.Address + if author == nil { + beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation + } else { + beneficiary = *author + } return vm.Context{ CanTransfer: CanTransfer, Transfer: Transfer, GetHash: GetHashFn(header, chain), - Origin: msg.From(), - Coinbase: header.Coinbase, + Coinbase: beneficiary, BlockNumber: new(big.Int).Set(header.Number), Time: new(big.Int).Set(header.Time), Difficulty: new(big.Int).Set(header.Difficulty), @@ -48,7 +59,7 @@ func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Co } // GetHashFn returns a GetHashFunc which retrieves header hashes by number -func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash { +func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash { return func(n uint64) common.Hash { for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { if header.Number.Uint64() == n { diff --git a/core/gaspool.go b/core/gaspool.go index f1c64c97a..ef99908cf 100644 --- a/core/gaspool.go +++ b/core/gaspool.go @@ -35,7 +35,7 @@ func (gp *GasPool) AddGas(amount *big.Int) *GasPool { func (gp *GasPool) SubGas(amount *big.Int) error { i := (*big.Int)(gp) if i.Cmp(amount) < 0 { - return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount} + return ErrGasLimitReached } i.Sub(i, amount) return nil diff --git a/core/gen_genesis.go b/core/gen_genesis.go new file mode 100644 index 000000000..3f83905c9 --- /dev/null +++ b/core/gen_genesis.go @@ -0,0 +1,102 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package core + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/params" +) + +func (g Genesis) MarshalJSON() ([]byte, error) { + type Genesis struct { + Config *params.ChainConfig `json:"config"` + Nonce math.HexOrDecimal64 `json:"nonce"` + Timestamp math.HexOrDecimal64 `json:"timestamp"` + ParentHash common.Hash `json:"parentHash"` + ExtraData hexutil.Bytes `json:"extraData"` + GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` + Mixhash common.Hash `json:"mixHash"` + Coinbase common.Address `json:"coinbase"` + Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` + } + var enc Genesis + enc.Config = g.Config + enc.Nonce = math.HexOrDecimal64(g.Nonce) + enc.Timestamp = math.HexOrDecimal64(g.Timestamp) + enc.ParentHash = g.ParentHash + enc.ExtraData = g.ExtraData + enc.GasLimit = math.HexOrDecimal64(g.GasLimit) + enc.Difficulty = (*math.HexOrDecimal256)(g.Difficulty) + enc.Mixhash = g.Mixhash + enc.Coinbase = g.Coinbase + if g.Alloc != nil { + enc.Alloc = make(map[common.UnprefixedAddress]GenesisAccount, len(g.Alloc)) + for k, v := range g.Alloc { + enc.Alloc[common.UnprefixedAddress(k)] = v + } + } + return json.Marshal(&enc) +} + +func (g *Genesis) UnmarshalJSON(input []byte) error { + type Genesis struct { + Config *params.ChainConfig `json:"config"` + Nonce *math.HexOrDecimal64 `json:"nonce"` + Timestamp *math.HexOrDecimal64 `json:"timestamp"` + ParentHash *common.Hash `json:"parentHash"` + ExtraData hexutil.Bytes `json:"extraData"` + GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"` + Mixhash *common.Hash `json:"mixHash"` + Coinbase *common.Address `json:"coinbase"` + Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"` + } + var dec Genesis + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Config != nil { + g.Config = dec.Config + } + if dec.Nonce != nil { + g.Nonce = uint64(*dec.Nonce) + } + if dec.Timestamp != nil { + g.Timestamp = uint64(*dec.Timestamp) + } + if dec.ParentHash != nil { + g.ParentHash = *dec.ParentHash + } + if dec.ExtraData != nil { + g.ExtraData = dec.ExtraData + } + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for Genesis") + } + g.GasLimit = uint64(*dec.GasLimit) + if dec.Difficulty == nil { + return errors.New("missing required field 'difficulty' for Genesis") + } + g.Difficulty = (*big.Int)(dec.Difficulty) + if dec.Mixhash != nil { + g.Mixhash = *dec.Mixhash + } + if dec.Coinbase != nil { + g.Coinbase = *dec.Coinbase + } + if dec.Alloc == nil { + return errors.New("missing required field 'alloc' for Genesis") + } + g.Alloc = make(GenesisAlloc, len(dec.Alloc)) + for k, v := range dec.Alloc { + g.Alloc[common.Address(k)] = v + } + return nil +} diff --git a/core/gen_genesis_account.go b/core/gen_genesis_account.go new file mode 100644 index 000000000..bc5fc936b --- /dev/null +++ b/core/gen_genesis_account.go @@ -0,0 +1,55 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package core + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +func (g GenesisAccount) MarshalJSON() ([]byte, error) { + type GenesisAccount struct { + Code hexutil.Bytes `json:"code,omitempty"` + Storage map[common.Hash]common.Hash `json:"storage,omitempty"` + Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` + Nonce math.HexOrDecimal64 `json:"nonce,omitempty"` + } + var enc GenesisAccount + enc.Code = g.Code + enc.Storage = g.Storage + enc.Balance = (*math.HexOrDecimal256)(g.Balance) + enc.Nonce = math.HexOrDecimal64(g.Nonce) + return json.Marshal(&enc) +} + +func (g *GenesisAccount) UnmarshalJSON(input []byte) error { + type GenesisAccount struct { + Code hexutil.Bytes `json:"code,omitempty"` + Storage map[common.Hash]common.Hash `json:"storage,omitempty"` + Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` + Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"` + } + var dec GenesisAccount + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Code != nil { + g.Code = dec.Code + } + if dec.Storage != nil { + g.Storage = dec.Storage + } + if dec.Balance == nil { + return errors.New("missing required field 'balance' for GenesisAccount") + } + g.Balance = (*big.Int)(dec.Balance) + if dec.Nonce != nil { + g.Nonce = uint64(*dec.Nonce) + } + return nil +} diff --git a/core/genesis.go b/core/genesis.go index 9fa49f4d0..883cea2fd 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -17,230 +17,286 @@ package core import ( - "compress/bzip2" - "compress/gzip" - "encoding/base64" - "encoding/json" + "errors" "fmt" - "io" - "io/ioutil" "math/big" "strings" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" ) -// WriteGenesisBlock writes the genesis block to the database as block number 0 -func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, error) { - contents, err := ioutil.ReadAll(reader) - if err != nil { - return nil, err - } +//go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go +//go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go - var genesis struct { - ChainConfig *params.ChainConfig `json:"config"` - Nonce string - Timestamp string - ParentHash string - ExtraData string - GasLimit string - Difficulty string - Mixhash string - Coinbase string - Alloc map[string]struct { - Code string - Storage map[string]string - Balance string - Nonce string - } - } +var errGenesisNoConfig = errors.New("genesis has no chain configuration") - if err := json.Unmarshal(contents, &genesis); err != nil { - return nil, err - } - if genesis.ChainConfig == nil { - genesis.ChainConfig = params.AllProtocolChanges +// Genesis specifies the header fields, state of a genesis block. It also defines hard +// fork switch-over blocks through the chain configuration. +type Genesis struct { + Config *params.ChainConfig `json:"config"` + Nonce uint64 `json:"nonce"` + Timestamp uint64 `json:"timestamp"` + ParentHash common.Hash `json:"parentHash"` + ExtraData []byte `json:"extraData"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` + Mixhash common.Hash `json:"mixHash"` + Coinbase common.Address `json:"coinbase"` + Alloc GenesisAlloc `json:"alloc" gencodec:"required"` +} + +// GenesisAlloc specifies the initial state that is part of the genesis block. +type GenesisAlloc map[common.Address]GenesisAccount + +// GenesisAccount is an account in the state of the genesis block. +type GenesisAccount struct { + Code []byte `json:"code,omitempty"` + Storage map[common.Hash]common.Hash `json:"storage,omitempty"` + Balance *big.Int `json:"balance" gencodec:"required"` + Nonce uint64 `json:"nonce,omitempty"` +} + +// field type overrides for gencodec +type genesisSpecMarshaling struct { + Nonce math.HexOrDecimal64 + Timestamp math.HexOrDecimal64 + ExtraData hexutil.Bytes + GasLimit math.HexOrDecimal64 + Difficulty *math.HexOrDecimal256 + Alloc map[common.UnprefixedAddress]GenesisAccount +} +type genesisAccountMarshaling struct { + Code hexutil.Bytes + Balance *math.HexOrDecimal256 + Nonce math.HexOrDecimal64 +} + +// GenesisMismatchError is raised when trying to overwrite an existing +// genesis block with an incompatible one. +type GenesisMismatchError struct { + Stored, New common.Hash +} + +func (e *GenesisMismatchError) Error() string { + return fmt.Sprintf("wrong genesis block in database (have %x, new %x)", e.Stored[:8], e.New[:8]) +} + +// SetupGenesisBlock writes or updates the genesis block in db. +// The block that will be used is: +// +// genesis == nil genesis != nil +// +------------------------------------------ +// db has no genesis | main-net default | genesis +// db has genesis | from DB | genesis (if compatible) +// +// The stored chain configuration will be updated if it is compatible (i.e. does not +// specify a fork block below the local head block). In case of a conflict, the +// error is a *params.ConfigCompatError and the new, unwritten config is returned. +// +// The returned chain configuration is never nil. +func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { + if genesis != nil && genesis.Config == nil { + return params.AllProtocolChanges, common.Hash{}, errGenesisNoConfig } - // creating with empty hash always works - statedb, _ := state.New(common.Hash{}, chainDb) - for addr, account := range genesis.Alloc { - balance, ok := math.ParseBig256(account.Balance) - if !ok { - return nil, fmt.Errorf("invalid balance for account %s: %q", addr, account.Balance) - } - nonce, ok := math.ParseUint64(account.Nonce) - if !ok { - return nil, fmt.Errorf("invalid nonce for account %s: %q", addr, account.Nonce) + // Just commit the new block if there is no stored genesis block. + stored := GetCanonicalHash(db, 0) + if (stored == common.Hash{}) { + if genesis == nil { + log.Info("Writing default main-net genesis block") + genesis = DefaultGenesisBlock() + } else { + log.Info("Writing custom genesis block") } + block, err := genesis.Commit(db) + return genesis.Config, block.Hash(), err + } - address := common.HexToAddress(addr) - statedb.AddBalance(address, balance) - statedb.SetCode(address, common.FromHex(account.Code)) - statedb.SetNonce(address, nonce) - for key, value := range account.Storage { - statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) + // Check whether the genesis block is already written. + if genesis != nil { + block, _ := genesis.ToBlock() + hash := block.Hash() + if hash != stored { + return genesis.Config, block.Hash(), &GenesisMismatchError{stored, hash} } } - root, stateBatch := statedb.CommitBatch(false) - difficulty, ok := math.ParseBig256(genesis.Difficulty) - if !ok { - return nil, fmt.Errorf("invalid difficulty: %q", genesis.Difficulty) + // Get the existing chain configuration. + newcfg := genesis.configOrDefault(stored) + storedcfg, err := GetChainConfig(db, stored) + if err != nil { + if err == ChainConfigNotFoundErr { + // This case happens if a genesis write was interrupted. + log.Warn("Found genesis block without chain config") + err = WriteChainConfig(db, stored, newcfg) + } + return newcfg, stored, err } - gaslimit, ok := math.ParseUint64(genesis.GasLimit) - if !ok { - return nil, fmt.Errorf("invalid gas limit: %q", genesis.GasLimit) + // Special case: don't change the existing config of a non-mainnet chain if no new + // config is supplied. These chains would get AllProtocolChanges (and a compat error) + // if we just continued here. + if genesis == nil && stored != params.MainNetGenesisHash { + return storedcfg, stored, nil } - nonce, ok := math.ParseUint64(genesis.Nonce) - if !ok { - return nil, fmt.Errorf("invalid nonce: %q", genesis.Nonce) + + // Check config compatibility and write the config. Compatibility errors + // are returned to the caller unless we're already at block zero. + height := GetBlockNumber(db, GetHeadHeaderHash(db)) + if height == missingNumber { + return newcfg, stored, fmt.Errorf("missing block number for head header hash") } - timestamp, ok := math.ParseBig256(genesis.Timestamp) - if !ok { - return nil, fmt.Errorf("invalid timestamp: %q", genesis.Timestamp) + compatErr := storedcfg.CheckCompatible(newcfg, height) + if compatErr != nil && height != 0 && compatErr.RewindTo != 0 { + return newcfg, stored, compatErr } + return newcfg, stored, WriteChainConfig(db, stored, newcfg) +} - block := types.NewBlock(&types.Header{ - Nonce: types.EncodeNonce(nonce), - Time: timestamp, - ParentHash: common.HexToHash(genesis.ParentHash), - Extra: common.FromHex(genesis.ExtraData), - GasLimit: new(big.Int).SetUint64(gaslimit), - Difficulty: difficulty, - MixDigest: common.HexToHash(genesis.Mixhash), - Coinbase: common.HexToAddress(genesis.Coinbase), - Root: root, - }, nil, nil, nil) +func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { + switch { + case g != nil: + return g.Config + case ghash == params.MainNetGenesisHash: + return params.MainnetChainConfig + case ghash == params.TestNetGenesisHash: + return params.TestnetChainConfig + default: + return params.AllProtocolChanges + } +} - if block := GetBlock(chainDb, block.Hash(), block.NumberU64()); block != nil { - log.Info("Genesis block known, writing canonical number") - err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) - if err != nil { - return nil, err +// ToBlock creates the block and state of a genesis specification. +func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) { + db, _ := ethdb.NewMemDatabase() + statedb, _ := state.New(common.Hash{}, db) + for addr, account := range g.Alloc { + statedb.AddBalance(addr, account.Balance) + statedb.SetCode(addr, account.Code) + statedb.SetNonce(addr, account.Nonce) + for key, value := range account.Storage { + statedb.SetState(addr, key, value) } - return block, nil } + root := statedb.IntermediateRoot(false) + head := &types.Header{ + Nonce: types.EncodeNonce(g.Nonce), + Time: new(big.Int).SetUint64(g.Timestamp), + ParentHash: g.ParentHash, + Extra: g.ExtraData, + GasLimit: new(big.Int).SetUint64(g.GasLimit), + Difficulty: g.Difficulty, + MixDigest: g.Mixhash, + Coinbase: g.Coinbase, + Root: root, + } + if g.GasLimit == 0 { + head.GasLimit = params.GenesisGasLimit + } + if g.Difficulty == nil { + head.Difficulty = params.GenesisDifficulty + } + return types.NewBlock(head, nil, nil, nil), statedb +} - if err := stateBatch.Write(); err != nil { +// Commit writes the block and state of a genesis specification to the database. +// The block is committed as the canonical head block. +func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { + block, statedb := g.ToBlock() + if _, err := statedb.CommitTo(db, false); err != nil { return nil, fmt.Errorf("cannot write state: %v", err) } - if err := WriteTd(chainDb, block.Hash(), block.NumberU64(), difficulty); err != nil { + if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil { return nil, err } - if err := WriteBlock(chainDb, block); err != nil { + if err := WriteBlock(db, block); err != nil { return nil, err } - if err := WriteBlockReceipts(chainDb, block.Hash(), block.NumberU64(), nil); err != nil { + if err := WriteBlockReceipts(db, block.Hash(), block.NumberU64(), nil); err != nil { return nil, err } - if err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()); err != nil { + if err := WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil { return nil, err } - if err := WriteHeadBlockHash(chainDb, block.Hash()); err != nil { + if err := WriteHeadBlockHash(db, block.Hash()); err != nil { return nil, err } - if err := WriteChainConfig(chainDb, block.Hash(), genesis.ChainConfig); err != nil { + if err := WriteHeadHeaderHash(db, block.Hash()); err != nil { return nil, err } - return block, nil -} - -// GenesisBlockForTesting creates a block in which addr has the given wei balance. -// The state trie of the block is written to db. the passed db needs to contain a state root -func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { - statedb, _ := state.New(common.Hash{}, db) - obj := statedb.GetOrNewStateObject(addr) - obj.SetBalance(balance) - root, err := statedb.Commit(false) - if err != nil { - panic(fmt.Sprintf("cannot write state: %v", err)) + config := g.Config + if config == nil { + config = params.AllProtocolChanges } - block := types.NewBlock(&types.Header{ - Difficulty: params.GenesisDifficulty, - GasLimit: params.GenesisGasLimit, - Root: root, - }, nil, nil, nil) - return block + return block, WriteChainConfig(db, block.Hash(), config) } -type GenesisAccount struct { - Address common.Address - Balance *big.Int -} - -func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount) *types.Block { - accountJson := "{" - for i, account := range accounts { - if i != 0 { - accountJson += "," - } - accountJson += fmt.Sprintf(`"0x%x":{"balance":"%d"}`, account.Address, account.Balance) - } - accountJson += "}" - - testGenesis := fmt.Sprintf(`{ - "nonce":"0x%x", - "gasLimit":"0x%x", - "difficulty":"0x%x", - "alloc": %s -}`, types.EncodeNonce(0), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes(), accountJson) - block, err := WriteGenesisBlock(db, strings.NewReader(testGenesis)) +// MustCommit writes the genesis block and state to db, panicking on error. +// The block is committed as the canonical head block. +func (g *Genesis) MustCommit(db ethdb.Database) *types.Block { + block, err := g.Commit(db) if err != nil { panic(err) } return block } -// WriteDefaultGenesisBlock assembles the official Ethereum genesis block and -// writes it - along with all associated state - into a chain database. -func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { - return WriteGenesisBlock(chainDb, strings.NewReader(DefaultGenesisBlock())) +// GenesisBlockForTesting creates and writes a block in which addr has the given wei balance. +func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { + g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}} + return g.MustCommit(db) } -// WriteTestNetGenesisBlock assembles the test network genesis block and -// writes it - along with all associated state - into a chain database. -func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { - return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock())) +// DefaultGenesisBlock returns the Ethereum main net genesis block. +func DefaultGenesisBlock() *Genesis { + return &Genesis{ + Config: params.MainnetChainConfig, + Nonce: 66, + ExtraData: hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), + GasLimit: 5000, + Difficulty: big.NewInt(17179869184), + Alloc: decodePrealloc(mainnetAllocData), + } } -// DefaultGenesisBlock assembles a JSON string representing the default Ethereum -// genesis block. -func DefaultGenesisBlock() string { - reader, err := gzip.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultGenesisBlock))) - if err != nil { - panic(fmt.Sprintf("failed to access default genesis: %v", err)) - } - blob, err := ioutil.ReadAll(reader) - if err != nil { - panic(fmt.Sprintf("failed to load default genesis: %v", err)) +// DefaultTestnetGenesisBlock returns the Ropsten network genesis block. +func DefaultTestnetGenesisBlock() *Genesis { + return &Genesis{ + Config: params.TestnetChainConfig, + Nonce: 66, + ExtraData: hexutil.MustDecode("0x3535353535353535353535353535353535353535353535353535353535353535"), + GasLimit: 16777216, + Difficulty: big.NewInt(1048576), + Alloc: decodePrealloc(testnetAllocData), } - return string(blob) } -// DefaultTestnetGenesisBlock assembles a JSON string representing the default Ethereum -// test network genesis block. -func DefaultTestnetGenesisBlock() string { - reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultTestnetGenesisBlock))) - blob, err := ioutil.ReadAll(reader) - if err != nil { - panic(fmt.Sprintf("failed to load default genesis: %v", err)) +// DevGenesisBlock returns the 'geth --dev' genesis block. +func DevGenesisBlock() *Genesis { + return &Genesis{ + Config: params.AllProtocolChanges, + Nonce: 42, + GasLimit: 4712388, + Difficulty: big.NewInt(131072), + Alloc: decodePrealloc(devAllocData), } - return string(blob) } -// DevGenesisBlock assembles a JSON string representing a local dev genesis block. -func DevGenesisBlock() string { - reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultDevnetGenesisBlock))) - blob, err := ioutil.ReadAll(reader) - if err != nil { - panic(fmt.Sprintf("failed to load dev genesis: %v", err)) +func decodePrealloc(data string) GenesisAlloc { + var p []struct{ Addr, Balance *big.Int } + if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil { + panic(err) + } + ga := make(GenesisAlloc, len(p)) + for _, account := range p { + ga[common.BigToAddress(account.Addr)] = GenesisAccount{Balance: account.Balance} } - return string(blob) + return ga } diff --git a/core/genesis_alloc.go b/core/genesis_alloc.go new file mode 100644 index 000000000..fa4635df2 --- /dev/null +++ b/core/genesis_alloc.go @@ -0,0 +1,25 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +// Constants containing the genesis allocation of built-in genesis blocks. +// Their content is an RLP-encoded list of (address, balance) tuples. +// Use mkalloc.go to create/update them. + +const mainnetAllocData = "\xfa\x04]X\u0793\r\x83b\x011\x8e\u0189\x9agT\x06\x908'\x80t2\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x17bC\x0e\xa9\u00e2nWI\xaf\xdbp\xda_x\u077b\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0793\x1d\x14\x80K9\x9cn\xf8\x0edWoev`\x80O\xec\v\x89\u3bb5sr@\xa0\x00\x00\u07932@5\x87\x94{\x9f\x15b*h\xd1\x04\xd5M3\xdb\xd1\u0349\x043\x87Oc,\xc6\x00\x00\u0793I~\x92\xcd\xc0\xe0\xb9c\xd7R\xb2)j\u02c7\u0682\x8b$\x89\n\x8fd\x9f\xe7\xc6\x18\x00\x00\u0793K\xfb\xe1Tk\xc6\xc6[\\~\xaaU0K8\xbb\xfe\xc6\u04c9lk\x93[\x8b\xbd@\x00\x00\u0793Z\x9c\x03\xf6\x9d\x17\xd6l\xbb\x8a\xd7!\x00\x8a\x9e\xbb\xb86\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u0793]\x0e\xe8\x15^\xc0\xa6\xffh\bU,\xa5\xf1k\xb5\xbe2:\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u0793v\"\xd8J#K\xb8\xb0x#\x0f\u03c4\xb6z\u9a2c\xae\x89%\xe1\xccQ\x99R\xf8\x00\x00\u0793{\x9f\xc3\x19\x05\xb4\x99K\x04\xc9\xe2\xcf\xdc^'pP?B\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u0793\u007fJ#\xca\x00\xcd\x04=%\u0088\x8c\x1a\xa5h\x8f\x81\xa3D\x89)\xf0\xa9[\xfb\xf7)\x00\x00\u0793\x869\u06bb\u3bac\x88{]\xc0\xe4>\x13\xbc\u0487\xd7l\x89\x10\xd0\xe3\xc8}n,\x00\x00\u0793\x89P\x86y\xab\xf8\xc7\x1b\xf6x\x16\x87\x12\x0e>j\x84XM\x89a\x94\x04\x9f0\xf7 \x00\x00\u0793\x8f\xc7\u02ed\xff\xbd\r\u007f\xe4O\x8d\xfd`\xa7\x9dr\x1a\x1c\x9c\x8965\u026d\xc5\u07a0\x00\x00\u0793\x95`\xa3\xdebxh\xf9\x1f\xa8\xbf\xe1\xc1\xb7\xaf\xaf\b\x18k\x89\x1cg\xf5\xf7\xba\xa0\xb0\x00\x00\u0793\x96\x97G\xf7\xa5\xb3\x06E\xfe\x00\xe4I\x01CZ\xce$\xcc7\x89\\(=A\x03\x94\x10\x00\x00\u0793\x9am}\xb3&g\x9bw\xc9\x03\x91\xa7Gm#\x8f;\xa3>\x89\n\xdaUGK\x814\x00\x00\u0793\x9e\xef\n\b\x86\x05n?i!\x18S\xb9\xb7E\u007f7\x82\u4262\xa8x\x06\x9b(\xe0\x00\x00\u0793\x9f\xdb\xf4N\x1fJcb\xb7i\u00daG_\x95\xa9l+\u01c9\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d3\xa5y\u007fR\xc9\u054f\x18\x9f6\xb1\xd4]\x1b\xf6\x04\x1f/k\x8a\x01'\u0473F\x1a\xcd\x1a\x00\x00\u0793\xaaS\x81\xb2\x13\x8e\xbe\xff\xc1\x91\xd5\xd8\u00d1u;p\x98\u04895\xab\xb0\x9f\xfe\u07b6\x80\x00\u0793\xaa\xda%\xea\"\x86p\x9a\xbbB-A\x92?\u04c0\xcd\x04\u01c9#=\xf3)\x9far\x00\x00\u0793\xac\xbf\xb2\xf2ZT\x85\xc79\xefp\xa4N\xee\xeb|e\xa6o\x89\x05k\xc7^-c\x10\x00\x00\u07d3\xac\xc6\xf0\x82\xa4B\x82\x87d\xd1\x1fX\u0589J\xe4\b\xf0s\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u0793\xb2w\xb0\x99\xa8\xe8f\xca\x0e\xc6[\u02c7(O\xd1B\xa5\x82\x89j\xcb=\xf2~\x1f\x88\x00\x00\u0753\xbd\xd4\x01:\xa3\x1c\x04al+\xc9x_'\x88\xf9\x15g\x9b\x88\xb9\xf6]\x00\xf6<\x00\x00\u0793\xc2}c\xfd\xe2K\x92\xee\x8a\x1e~\xd5\xd2m\x8d\xc5\xc8;\x03\x89lk\x93[\x8b\xbd@\x00\x00\u0553\xc4\x0f\xe2\tT#P\x9b\x9f\u0677T21X\xaf#\x10\xf3\x80\u0793\xd7^\xd6\fwO\x8b:ZQs\xfb\x183\xadq\x05\xa2\u0649l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d3\u05cd\x89\xb3_G'\x16\xec\xea\xfe\xbf`\x05'\u04e1\xf9i\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u0793\xda\xe2{5\v\xae \xc5e!$\xaf]\x8b\\\xba\x00\x1e\xc1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u0793\xdc\x01\xcb\xf4Ix\xa4.\x8d\xe8\xe46\xed\xf9B\x05\u03f6\xec\x89O\x0f\xeb\xbc\u068c\xb4\x00\x00\u07d3\u607c-\x10\xdbb\u0785\x84\x83$I\"P4\x8e\x90\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d3\xf4c\xe17\xdc\xf6%\xfb\U000fc8de\u0298\u04b9h\xcf\u007f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\x01\x00\a9K\x8bue\xa1e\x8a\xf8\x8c\xe4cI\x915\u05b7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x01\r\xf1\xdfK\xed#v\r-\x1c\x03x\x15\x86\xdd\xf7\x91\x8eT\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x01\x0fJ\x98\u07e1\xd9y\x9b\xf5\u01d6\xfbU\x0e\xfb\xe7\xec\xd8w\x8a\x01\xb2\xf2\x92#b\x92\xc7\x00\x00\u07d4\x01\x15PW\x00/k\r\x18\xac\xb98\x8d;\xc8\x12\x9f\x8fz \x89H\xa4<T`/p\x00\x00\u07d4\x01\"n\n\xd8\xd6\"w\xb1bb\x1cb\xc9(\xe9n\v\x9a\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01&\xe1.\xbc\x17\x03_5\xc0\xe9\xd1\x1d\xd1H9<@]z\x89lf\x06E\xaaG\x18\x00\x00\u07d4\x01/9j+^\xb85Y\xba\xc5\x15\xe5!\r\xf2\xc8\xc3b\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x014\xff8\x15_\xab\xae\x94\xfd5\xc4\xff\xe1\u05dd\xe7\xef\x9cY\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x016\xa5\xafl2\x99\u01b5\xf0\x05\xfd\xad\xdb\x14\x8c\a\v)\x9b\x89\x01\x1a\xa9\xac\x15\xf1(\x00\x00\u07d4\x01H\x8a\xd3\xda`<L\xddl\xb0\xb7\xa1\xe3\r*0\xc8\xfc8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01It\xa1\xf4k\xf2\x04\x94J\x851\x11\xe5/\x16\x02a}\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01K\u007fg\xb1O]\x98=\x87\x01OW\f\x8b\x99;\x98r\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01Q\xfa]\x17\xa2\xdc\xe2\xd7\xf1\xeb9\xef\u007f\xe2\xad!=]\x89\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x01Wz\xfdNP\x89\x02G\u0271\rD\xafs\"\x9a\xec\x88O\x89$\xdc\xe5M4\xa1\xa0\x00\x00\xe0\x94\x01_\t}\x9a\xcd\xdc\u076f\xaf*\x10~\xb9:@\xfc\x94\xb0L\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x01i\xc1\xc2\x10\xea\xe8E\xe5h@A.\x1fe\x99>\xa9\x0f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01k`\xbbmg\x92\x8c)\xfd\x03\x13\xc6f\u068f\x16\x98\xd9\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\x01l\x85\xe1a;\x90\x0f\xa3W\xb8(;\x12\x0ee\xae\xfc\xdd\b\x89+]\x97\x84\xa9|\xd5\x00\x00\u07d4\x01\x84\x92H\x8b\xa1\xa2\x924\"G\xb3\x18U\xa5Y\x05\xfe\xf2i\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x01\x8f \xa2{'\xecD\x1a\xf7#\xfd\x90\x99\xf2\u02f7\x9dbc\x89uy*\x8a\xbd\xef|\x00\x00\u07d4\x01\x91\xebT~{\xf6\x97k\x9b\x1bWuFv\x1d\xe6V\"\xe2\x89lkLM\xa6\u077e\x00\x00\u07d4\x01\x9dp\x95y\xffK\xc0\x9f\xdc\xdd\xe41\xdc\x14G\xd2\xc2`\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x01\xa2Z_Z\xf0\x16\x9b0\x86L;\xe4\xd7V<\xcdD\xf0\x9e\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\x01\xa7\xd9\xfa}\x0e\xb1\x18\\g\xe5M\xa8<.u\xdbi\u37ca\x01\x9dJ\xdd\xd0\u063c\x96\x00\x00\u07d4\x01\xa8\x18\x13ZAB\x10\xc3|b\xb6%\xac\xa1\xa5F\x11\xac6\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x01\xb1\xca\xe9\x1a;\x95Y\xaf\xb3<\xdcmh\x94B\xfd\xbf\xe07\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xb5\xb5\xbcZ\x11\u007f\xa0\x8b4\xed\x1d\xb9D\x06\bYz\xc5H\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x01\xbb\xc1Og\xaf\x069\xaa\xb1D\x1ej\b\xd4\xceqb\t\x0f\x89F\xfc\xf6\x8f\xf8\xbe\x06\x00\x00\xe0\x94\x01\xd08\x15\xc6\x1fAkq\xa2a\n-\xab\xa5\x9f\xf6\xa6\xde[\x8a\x02\x05\xdf\xe5\v\x81\xc8.\x00\x00\u07d4\x01\u0559\xee\r_\x8c8\xab-9.,e\xb7L<\xe3\x18 \x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\x01\xe4\x05!\x12%0\u066c\x91\x11<\x06\xa0\x19\vmc\x85\v\x89Hz\x9a0E9D\x00\x00\u07d4\x01\xe6A]X{\x06T\x90\xf1\xed\u007f!\xd6\xe0\xf3\x86\xeegG\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x01\xe8d\xd3Tt\x1bB>oB\x85\x17$F\x8ct\xf5\xaa\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x01\xed_\xba\x8d.\xabg:\xec\x04-0\xe4\xe8\xa6\x11\xd8\xc5Z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x01\xfb\x8e\xc1$%\xa0O\x81>F\xc5L\x05t\x8c\xa6\xb2\x9a\xa9\x89\x0e\x15s\x03\x85F|\x00\x00\u07d4\x01\xff\x1e\xb1\u07adP\xa7\xf2\xf9c\x8f\xde\xe6\xec\xcf:{*\u0209 \x86\xac5\x10R`\x00\x00\u07d4\x02\x03b\u00ed\xe8x\u0290\u05b2\u0609\xa4\xccU\x10\xee\xd5\xf3\x898\x88\xe8\xb3\x11\xad\xb3\x80\x00\u07d4\x02\x03\xae\x01\xd4\xc4\x1c\xae\x18e\xe0K\x1f[S\xcd\xfa\xec\xae1\x896\x89\xcd\u03b2\x8c\xd7\x00\x00\u07d4\x02\b\x93a\xa3\xfetQ\xfb\x1f\x87\xf0\x1a-\x86fS\xdc\v\a\x89\x02*\xc7H2\xb5\x04\x00\x00\u07d4\x02\x1fi\x04=\xe8\x8cI\x17\xca\x10\xf1\x84(\x97\xee\xc0X\x9c|\x89kD\u03f8\x14\x87\xf4\x00\x00\u07d4\x02)\x0f\xb5\xf9\xa5\x17\xf8(E\xac\xde\xca\x0f\xc8F\x03\x9b\xe23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x029\xb4\xf2\x1f\x8e\x05\xcd\x01Q++\xe7\xa0\xe1\x8am\x97F\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x02Gr\x12\xff\xddu\xe5\x15VQ\xb7e\x06\xb1dfq\xa1\xeb\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x02J\t\x8a\xe7\x02\xbe\xf5@l\x9c\"\xb7\x8b\xd4\xeb,\u01e2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x02K\xdd,{\xfdP\x0e\xe7@O\u007f\xb3\xe9\xfb1\xdd \xfb\u0449\t\xc2\x00vQ\xb2P\x00\x00\u07d4\x02Sg\x96\x03\x04\xbe\xee4Y\x11\x18\xe9\xac-\x13X\xd8\x02\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x02V\x14\x9f[Pc\xbe\xa1N\x15f\x1f\xfbX\xf9\xb4Y\xa9W\x89&)\xf6n\fS\x00\x00\x00\u07d4\x02`=z;\xb2\x97\xc6|\x87~]4\xfb\u0579\x13\xd4\xc6:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x02a\xad:\x17*\xbf\x13\x15\xf0\xff\xec2p\x98j\x84\t\xcb%\x89\v\b!;\u03cf\xfe\x00\x00\u07d4\x02d2\xaf7\xdcQ\x13\xf1\xf4mH\nM\u0c80R#~\x89\x13I\xb7\x86\xe4\v\xfc\x00\x00\u07d4\x02f\xab\x1ck\x02\x16#\v\x93\x95D=_\xa7^hEh\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x02u\x1d\u018c\xb5\xbdsp'\xab\xf7\u0777s\x90\xcdw\xc1k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x02w\x8e9\x0f\xa1u\x10\xa3B\x8a\xf2\x87\fBsT}8l\x8a\x03l<f\x17\f\rr\x00\x00\xe0\x94\x02\xad\xe5\xdb\"\xf8\xb7X\xee\x14Cbld\xec/2\xaa\n\x15\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x02\xaf$Y\xa9=\v?M\x06&6#l\u0532\x9e;\xce\u03c9g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x02\xb1\xafr3\x9b*\"V8\x9f\xd6F\a\xde$\xf0\xde`\n\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x02\xb6C\xd6\xfa\xbdCz\x85\x1a\u033ey\xab\xb7\xfd\xe1&\xdc\u03ca\x01\x86P\x12|\xc3\u0700\x00\x00\xe0\x94\x02\xb6\xd6\\\xb0\v{6\xe1\xfb^\xd3c,L\xb2\n\x89A0\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x02\xb7\xb1\u05b3L\xe0S\xa4\x0e\xb6\\\u0524\xf7\xdd\xdd\x0e\x9f0\x89%\"H\u07b6\xe6\x94\x00\x00\xe0\x94\x02\xc9\xf7\x94\n{\x8bzA\v\xf8=\xc9\xc2#3\xd4']\u04ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x02\u0523\th\xa3\x9e+4\x98\u00e6\xa4\xedE\xc1\xc6dh\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x02\xdf\xcb\x17\xa1\xb8tA\x03ct\xb7b\xa5\xd3A\x8b\x1c\xb4\u0509H\xb0+\xa9\u047aF\x00\x00\u07d4\x02\xe4\xcb\"\xbeF%\x8a@\xe1mC8\xd8\x02\xff\xfd\x00\xc1Q\x89\x14\x96\x96\xea\u03ba\x81\x00\x00\u07d4\x02\xe8\x16\xaf\xc1\xb5\xc0\xf3\x98R\x13\x19Y\xd9F\xeb;\a\xb5\xad\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x02\xf7\xf6r\t\xb1j\x17U\fiLrX8\x19\xc8\vT\xad\x89\x05U\x93\x06\xa7\x8ap\x00\x00\xe0\x94\x03\ts\x80{/Bi\x14\xad\x00\x18\x12p\xac\xd2{\x8f\xf6\x1f\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\x03\ty#\xba\x15^\x16\xd8/:\xd3\xf6\xb8\x15T\b\x84\xb9,\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x03\x0f\xb3@\x1fr\xbd4\x18\xb7\xd1\xdau\xbf\x8cQ\x9d\xd7\a\u0709\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x03\x1e%\xdbQk\x0f\t\x9f\xae\xbf\xd9O\x89\f\xf9f`\x83k\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x03(Q\f\t\xdb\u0345\x19J\x98\xd6|3\xacI\xf2\xf9M`\x8a\x02TO\xaaw\x80\x90\xe0\x00\x00\u07d4\x03)\x18\x8f\b\x06W\xab:*\xfaR$g\x17\x82y\x83 \x85\x89\v\xbfQ\r\xdf\xcb&\x00\x00\u07d4\x031x&\xd1\xf7\n\xa4\xbd\u07e0\x9b\xe0\xc4\x10UR\xd25\x8b\x89\x02\x1auJm\xc5(\x00\x00\u07d4\x033p\x12\xae\x1d\u007f\xf3\xee\u007fi|@>w\x80\x18\x8b\xf0\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x037|\x0eUkd\x01\x03(\x9aa\x89\u1baecI4g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03IcM\u00a9\xe8\f?w!\xee+PF\xae\xaa\xed\xfb\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x03U\xbc\xac\xbd!D\x1e\x95\xad\xee\xdc0\xc1r\x18\u0224\b\u0389\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x03n\xef\xf5\xba\x90\xa6\x87\x9a\x14\xdf\xf4\xc5\x04;\x18\xca\x04`\u0249\x05k\xc7^-c\x10\x00\x00\xe0\x94\x03qKA\u04a6\xf7Q\x00\x8e\xf8\xddM+)\xae\u02b8\xf3n\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x03r\xe8RX.\t44J\x0f\xed!x0M\xf2]F(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03r\xeeU\b\xbf\x81c\xed(N^\xef\x94\xceMsg\xe5\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x03}\xd0V\xe7\xfd\xbdd\x1d\xb5\xb6\xbe\xa2\xa8x\n\x83\xfa\u1009\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\x03\x83#\xb1\x84\xcf\xf7\xa8*\xe2\u1f67y?\xe41\x9c\xa0\xbf\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x03\x87y\xca-\xbef>c\xdb?\xe7V\x83\xea\x0e\xc6.#\x83\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x03\x8eE\xea\xdd=\x88\xb8\u007f\xe4\u06b0fh\x05\"\xf0\xdf\xc8\xf9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x03\x92T\x9ar\u007f\x81eT)\u02d2\x8bR\x9f%\xdfM\x13\x85\x89\x01lC\xa0\xee\xa0t\x00\x00\u07d4\x03\x94\xb9\x0f\xad\xb8`O\x86\xf4?\xc1\xe3]1$\xb3*Y\x89\x89)j\xa1@'\x8ep\x00\x00\u0794\x03\x9ezN\xbc(N,\xcdB\xb1\xbd\xd6\v\xd6Q\x1c\x0fw\x06\x88\xf0\x15\xf2W6B\x00\x00\u07d4\x03\x9e\xf1\xceR\xfeyc\xf1f\u0562u\u0131\x06\x9f\xe3\xa82\x89\x15\xaf9\u4ab2t\x00\x00\u07d4\x03\xa2l\xfcL\x181op\u055e\x9e\x1ay\xee>\x8b\x96/L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xaab(\x81#m\xd0\xf4\x94\f$\xc3$\xff\x8b{~!\x86\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\x03\xafz\xd9\xd5\"<\xf7\xc8\xc1? \xdfg\xeb\xe5\xff\u017bA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xb0\xf1|\xd4F\x9d\xdc\u03f7\xdai~\x82\xa9\x1a_\x9ewt\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xb4\x1bQ\xf4\x1d\xf2\r\xd2y\xba\xe1\x8c\x12w_w\xadw\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x03\xbe[F)\xae\xfb\xbc\xab\x9d\xe2m9Wl\xb7\xf6\x91\xd7d\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x03\xc6G\xa9\xf9)\xb0x\x1f\xe9\xae\x01\u02a3\xe1\x83\xe8vw~\x89\x18*\xb7\xc2\f\xe5$\x00\x00\u07d4\x03\xc9\x1d\x92\x946\x03\xe7R >\x054\x0eV`\x13\xb9\x00E\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x03\xcbLOE\x16\xc4\xffy\xa1\xb6$O\xbfW.\x1c\u007f\xeay\x89\x94\x89#z\u06daP\x00\x00\u07d4\x03\u02d8\u05ec\xd8\x17\u079d\x88m\"\xfa\xb3\xf1\xb5}\x92\xa6\b\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x03\u031d-!\xf8k\x84\xac\x8c\xea\xf9q\u06e7\x8a\x90\xe6%p\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x03\xd1rO\xd0\x0eT\xaa\xbc\xd2\xde*\x91\xe8F+\x10I\xdd:\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\x03\xde\xdf\xcd\v<.\x17\xc7\x05\xda$\x87\x90\uf626\xbdWQ\x89Hz\x9a0E9D\x00\x00\u07d4\x03\u8c04SuW\xe7\t\xea\xe2\xe1\u1966\xbc\xe1\xef\x83\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x03\xeam&\u0400\xe5z\xee9&\xb1\x8e\x8e\xd7:N[(&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x03\xeb<\xb8`\xf6\x02\x8d\xa5T\xd3D\xa2\xbbZP\n\xe8\xb8o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x03\xeb\xc6?\xdaf`\xa4e\x04^#_\xben\\\xf1\x95s_\x89\a\xb0l\xe8\u007f\xddh\x00\x00\xe0\x94\x03\xefj\xd2\x0f\xf7\xbdO\x00+\xacX\xd4uD\u03c7\x9a\xe7(\x8a\x01u\xc7X\u0439n\\\x00\x00\u07d4\x03\xf7\xb9 \b\x81:\xe0\xa6v\xeb!(\x14\xaf\xab5\"\x10i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\x11p\xf5\x81\u0780\xe5\x8b*\x04\\\x8f|\x14\x93\xb0\x01\xb7\u02c90<t\xa1\xa36\x94\x00\x00\u07d4\x04\x13\xd0\xcfx\xc0\x01\x89\x8a7\x8b\x91\x8c\xd6\xe4\x98\xeaw<M\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x04$\x1bA\xec\xbd\v\xfd\xf1)^\x9dO\xa5\x9e\xa0\x9ela\x86\x89e_v\x94P\xbcx\x00\x00\u07d4\x047\a\a\x1e*\xe2\x1e\xed\x97x\x91\xdcy\xcd]\x8e\xe1\xc2\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\x04N\x851D\xe36D\x95\u799f\xa1\xd4j\xbe\xa3\xac\td\x89\x02\xab\"T\xb1\u071a\x80\x00\u07d4\x04U\xdc\xec\x8a\u007f\xc4F\x1b\xfd\u007f7Eo\xce?L<\xaa\u01c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x04^\xd7\xf6\xd9\xee\x9f%.\a2h\xdb\x02,c&\xad\xfc[\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x04cw\xf8d\xb0\x14?(!t\xa8\x92\xa7=>\xc8\xeca2\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\x04i\xe8\xc4@E\v\x0eQ&&\xfe\x81~gT\xa8\x15(0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04m'K\x1a\xf6\x15\xfbPZvJ\xd8\u0767p\xb1\xdb/=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x04}Z&\u05ed\x8f\x8ep`\x0fp\xa3\x98\u076a\x1c-\xb2o\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x04~\x87\xc8\xf7\xd1\xfc\xe3\xb0\x13S\xa8Xb\xa9H\xac\x04\x9f>\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x04\u007f\x9b\xf1R\x9d\xaf\x87\xd4\a\x17^o\x17\x1b^Y\xe9\xff>\x89#<\x8f\xe4'\x03\xe8\x00\x00\xe0\x94\x04\x85'2\xb4\xc6R\xf6\xc2\u53b3e\x87\xe6\nb\xda\x14\u06ca\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x04\x8a\x89p\xeaAE\xc6MU\x17\xb8\xde[F\xd0YZ\xad\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x04\x9c]K\xc6\xf2]NEli{R\xa0x\x11\xcc\u045f\xb1\x89\x10D\x00\xa2G\x0eh\x00\x00\u07d4\x04\xa1\xca\xda\x1c\xc7Q\b/\xf8\u0692\x8e<\xfa\x00\b \xa9\xe9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x04\xa8\n\xfa\xd5>\xf1\xf8Ae\xcf\xd8R\xb0\xfd\xf1\xb1\xc2K\xa8\x89\x03$\xe9d\xb3\xec\xa8\x00\x00\u07d4\x04\xaa\xfc\x8a\xe5\xceoI\x03\u021d\u007f\xac\x9c\xb1\x95\x12\"Gw\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x04\xbaK\xb8q@\x02,!Jo\xacB\xdbZ\x16\u0755@E\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xba\x8a?\x03\xf0\x8b\x89P\x95\x99M\xdaa\x9e\u06ac\xee>z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xc2\xc6K\xb5L>\xcc\xd0U\x85\xe1\x0e\xc6\xf9\x9a\f\xdb\x01\xa3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x04\xceE\xf6\x00\xdb\x18\xa9\u0405\x1b)\xd99>\xbd\xaa\xfe=\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x04\u05b8\xd4\u0686t\a\xbb\x99wI\u07bb\xcd\xc0\xb3XS\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x04\xd78\x96\xcfe\x93\xa6\x91\x97*\x13\xa6\xe4\x87\x1f\xf2\xc4+\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x04\xd8*\xf9\xe0\x1a\x93m\x97\xf8\xf8Y@\xb9p\xf9\xd4\u06d96\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x04\xe5\xf5\xbc|\x92?\xd1\xe3\x175\xe7.\xf9h\xfdg\x11\fn\x89WU\x1d\xbc\x8ebL\x00\x00\u07d4\x04\xec\xa5\x01c\n\xbc\xe3R\x18\xb1t\x95k\x89\x1b\xa2^\xfb#\x8966\x9e\xd7t}&\x00\x00\u07d4\x05\x05\xa0\x8e\"\xa1\t\x01Z\"\xf6\x850STf*U1\u0549\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x05\x14\x95L</\xb6W\xf9\xa0oQ\x0e\xa2'H\xf0'\xcd\u04c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x05\x163\b\r\a\xa5W\xad\xde1\x92a\xb0t\x99\u007f\x14i-\x8a\x01:k+VHq\xa0\x00\x00\u07d4\x05\x17D\x8d\xad\xa7a\xcc[\xa4\x03>\xe8\x81\xc807\x03d\x00\x89lO\xd1\xee$nx\x00\x00\u07d4\x05\x1dBBv\xb2\x129fQ\x86\x13=e;\xb8\xb1\x86/\x89\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05!\xbc:\x9f\x87\x11\xfe\xcb\x10\xf5\a\x97\xd7\x10\x83\xe3A\ub749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05#mL\x90\xd0e\xf9\u34c3X\xaa\xff\xd7w\xb8j\xecI\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x05*X\xe05\xf1\xfe\x9c\xdd\x16\x9b\xcf \x97\x03E\xd1+\x9cQ\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x05.\xab\x1fa\xb6\xd4U\x17(?A\xd1D\x18$\x87\x87I\u0409\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x053n\x9ar'(\xd9c\xe7\xa1\xcf'Y\xfd\x02tS\x0f\u02891\xa2D?\x88\x8ay\x80\x00\u07d4\x054q\u035aA\x92[9\x04\xa5\xa8\xff\xca6Y\xe04\xbe#\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4\x056\x1d\x8e\xb6\x94\x1dN\x90\xfb~\x14\x18\xa9Z2\xd5%w2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x05B:T\xc8\xd0\xf9p~pAs\xd9#\xb9F\xed\xc8\xe7\x00\x89\x06\xea\x03\u00bf\x8b\xa5\x80\x00\u07d4\x05D\f[\a;R\x9bH) \x9d\xff\x88\t\x0e\a\xc4\xf6\xf5\x89E\u04977\xe2/ \x00\x00\u07d4\x05Z\xb6X\xc6\xf0\xedO\x87^\xd6t.K\xc7)-\x1a\xbb\xf0\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x05[\xd0,\xaf\x19\xd6 +\xbc\u0703m\x18{\xd1\xc0\x1c\xf2a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05^\xacO\x1a\xd3\xf5\x8f\v\xd0$\u058e\xa6\r\xbe\x01\u01af\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x05fQU\xccI\xcb\xf6\xaa\xbd\u056e\x92\xcb\xfa\xad\x82\xb8\xc0\xc1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x05f\x86\a\x8f\xb6\xbc\xf9\xba\n\x8a\x8d\xc6:\x90o_\xea\xc0\xea\x89\x1b\x18\x1eK\xf24<\x00\x00\u07d4\x05iks\x91k\xd3\x03>\x05R\x1e2\x11\xdf\xec\x02n\x98\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x05k\x15F\x89O\x9a\x85\xe2\x03\xfb3m\xb5i\xb1l%\xe0O\x89\t.\xdb\t\xff\b\u0600\x00\u07d4\x05yI\xe1\xca\x05pF\x9eL\xe3\u0190\xaea:k\x01\xc5Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x05}\u049f-\x19\xaa=\xa4#'\xeaP\xbc\xe8o\xf5\xc9\x11\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x05\u007f\u007f\x81\xcdz@o\xc4Y\x94@\x8bPI\x91,Vdc\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x05\x91]N\"Zf\x81b\xae\xe7\xd6\xc2_\xcf\xc6\xed\x18\xdb\x03\x89\x03\x98\xc3ry%\x9e\x00\x00\u07d4\x05\x96\xa2}\xc3\xee\x11_\xce/\x94\xb4\x81\xbc z\x9e&\x15%\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x05\xa80rC\x02\xbc\x0fn\xbd\xaa\x1e\xbe\xee\xb4nl\xe0\v9\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\x05\xae\u007f\u053b\u0300\xca\x11\xa9\n\x1e\u01e3\x01\xf7\xcc\u0303\u06c91T\xc9r\x9d\x05x\x00\x00\u07d4\x05\xbbd\xa9\x16\xbef\xf4`\xf5\xe3\xb6C2\x11\r \x9e\x19\xae\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\x05\xbfO\xcf\xe7r\xe4[\x82dC\x85.l5\x13P\xcer\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\x05\xc6@\x04\xa9\xa8&\xe9N^N\xe2g\xfa*v2\xddNo\x8a\x03m\xc4.\xbf\xf9\v\u007f\x80\x00\xe0\x94\x05\xc76\xd3e\xaa7\xb5\xc0\xbe\x9c\x12\u022d\\\xd9\x03\xc3,\xf9\x8a\x01E^{\x80\n\x86\x88\x00\x00\xe0\x94\x05\xcbl;\x00r\xd3\x11ga\xb52\xb2\x18D;S\xe8\xf6\u014a\x1e\x02\xc3\xd7\xfc\xa9\xb6(\x00\x00\u07d4\x05\xd0\xf4\xd7(\xeb\xe8.\x84\xbfYu\x15\xadA\xb6\v\xf2\x8b9\x89\u3bb5sr@\xa0\x00\x00\u07d4\x05\u058d\xada\u04fb\u07f3\xf7y&\\IGJ\xff?\xcd0\x89\x02\"\xc5]\xc1Q\x9d\x80\x00\u07d4\x05\xe6q\xdeU\xaf\xec\x96K\aM\xe5t\xd5\x15\x8d]!\xb0\xa3\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x05\xe9{\tI,\u058fc\xb1+\x89.\xd1\xd1\x1d\x15,\x0e\u02897\b\xba\xed=h\x90\x00\x00\u07d4\x05\xf3c\x1fVd\xbd\xad]\x012\xc88\x8d6\xd7\u0612\t\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x06\t\xd8:l\xe1\xff\u0276\x90\xf3\xe9\xa8\x1e\x98>\x8b\xdcM\x9d\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4\x06\x1e\xa4\x87|\u0409D\xebd\u0096n\x9d\xb8\xde\xdc\xfe\xc0k\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06%\xd0`V\x96\x8b\x00\"\x06\xff\x91\x98\x01@$+\xfa\xa4\x99\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x06(\xbf\xbeU5x/\xb5\x88@k\xc9f`\xa4\x9b\x01\x1a\xf5\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x061\u044b\xbb\xbd0\xd9\xe1s+\xf3n\xda\xe2\u0389\x01\xab\x80\x89\xa3\xf9\x88U\xec9\x90\x00\x00\u07d4\x061\xdc@\xd7NP\x95\xe3r\x9e\xdd\xf4\x95D\xec\xd49og\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\x067Y\xdd\x1cN6.\xb1\x93\x98\x95\x1f\xf9\xf8\xfa\xd1\xd3\x10h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06_\xf5u\xfd\x9c\x16\xd3\xcbo\u058f\xfc\x8fH?\xc3.\xc85\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06a\x8e\x9dWb\xdfb\x02\x86\x01\xa8\x1dD\x87\u05a0\xec\xb8\x0e\x89Hz\x9a0E9D\x00\x00\xe0\x94\x06fG\xcf\xc8]#\xd3v\x05W= \x8c\xa1T\xb2D\xd7l\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x06xeJ\xc6v\x1d\xb9\x04\xa2\xf7\xe8Y^\xc1\xea\xacsC\b\x89/\x98\xb2\x9c(\x18\xf8\x00\x00\u07d4\x06\x86\n\x93RYU\xffbI@\xfa\xdc\xff\xb8\xe1I\xfdY\x9c\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94\x06\x8c\xe8\xbdn\x90*E\u02c3\xb5\x15A\xb4\x0f9\xc4F\x97\x12\x8a\x01\x1c\x0f\x9b\xadJF\xe0\x00\x00\u07d4\x06\x8e)\xb3\xf1\x91\xc8\x12\xa699\x18\xf7\x1a\xb93\xaehG\xf2\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x06\x8eeWf\xb9D\xfb&6\x19e\x87@\xb8P\xc9J\xfa1\x89\x01\xe8\u007f\x85\x80\x9d\xc0\x00\x00\u0794\x06\x96N-\x17\xe9\x18\x9f\x88\xa8 96\xb4\n\xc9nS<\x06\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x06\x99L\xd8:\xa2d\n\x97\xb2`\vA3\x9d\x1e\r>\xdel\x89\r\x8drkqw\xa8\x00\x00\u07d4\x06\x9e\u042bz\xa7}\xe5q\xf1a\x06\x05\x1d\x92\xaf\xe1\x95\xf2\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x06\xac&\xad\x92\u02c5\x9b\u0550]\xdc\xe4&j\xa0\xecP\xa9\u0149*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x06\xb0\xc1\xe3\u007fZ^\u013b\xf5\b@T\x8f\x9d:\xc0(\x88\x97\x89\xd8\u0602\u148e}\x00\x00\u07d4\x06\xb0\xff\x83@s\xcc\xe1\xcb\xc9\xeaU~\xa8{`Yc\u8d09\x10CV\x1a\x88)0\x00\x00\xe0\x94\x06\xb1\x06d\x9a\xa8\xc4!\xdd\xcd\x1b\x8c2\xcd\x04\x18\xcf0\xda\x1f\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x06\xb5\xed\xe6\xfd\xf1\xd6\xe9\xa3G!7\x9a\xea\xa1|q=\xd8*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x06\xcb\xfa\b\xcd\xd4\xfb\xa77\xba\xc4\a\xbe\x82$\xf4\xee\xf3X(\x89 +\xe5\xe88.\x8b\x80\x00\u07d4\x06\xd6\xcb0\x84\x81\xc36\xa6\xe1\xa2%\xa9\x12\xf6\xe65Y@\xa1\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x06\xdc\u007f\x18\xce\xe7\xed\xab[yS7\xb1\xdfj\x9e\x8b\u062eY\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x06\xf6\x8d\xe3\xd79\xdbA\x12\x1e\xac\xf7y\xaa\xda=\xe8v!\a\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x06\xf7\u070d\x1b\x94b\xce\xf6\xfe\xb13h\xa7\xe3\x97K\t\u007f\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\a\x01\xf9\xf1G\xecHhV\xf5\xe1\xb7\x1d\xe9\xf1\x17\xe9\x9e!\x05\x89\te\xdaq\u007f\u0578\x00\x00\u07d4\a\r]6L\xb7\xbb\xf8\"\xfc,\xa9\x1a5\xbd\xd4A\xb2\x15\u0549lk\x93[\x8b\xbd@\x00\x00\xe0\x94\a\x1d\xd9\r\x14\xd4\x1fO\xf7\xc4\x13\xc2B8\xd35\x9c\xd6\x1a\a\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4\a&\xc4.\x00\xf4T\x04\x83n\xb1\xe2\x80\xd0s\xe7\x05\x96\x87\xf5\x89X\x00>?\xb9G\xa3\x80\x00\xe0\x94\a'\xbe\n*\x00! H\xb5R\x0f\xbe\xfb\x95>\xbc\x9dT\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a)\xa8\xa4\xa5\xba#\xf5y\xd0\x02[\x1a\xd0\xf8\xa0\xd3\\\xdf\u048a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\a)\xb4\xb4|\t\xeb\x16\x15\x84d\u022a\u007f\xd9i\vC\x889\x89lh\xcc\u041b\x02,\x00\x00\u0794\a4\xa0\xa8\x1c\x95b\xf4\xd9\xe9\xe1\n\x85\x03\xda\x15\xdbF\xd7n\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\a<g\xe0\x9b\\q<R!\u0220\xc7\xf3\xf7Df\xc3G\xb0\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\a?\x1e\xd1\xc9\xc3\xe9\xc5*\x9b\x02I\xa5\xc1\u02a0W\x1f\xdf\x05\x89\x03\xd0\xff\v\x01;\x80\x00\x00\u07d4\aHq1E\xef\x83\xc3\xf0\xefM1\xd8#xo~\x9c\u0189\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\u07d4\a]\x15\xe2\xd3=\x8bO\xa7\u06e8\xb9\xe6\a\xf0J&\x1e4\v\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\aea\xa8VE]~\xf8nc\xf8|s\u06f6(\xa5_E\x890\xca\x02O\x98{\x90\x00\x00\u07d4\an\xe9\x9d5Hb:\x03\xb5\xf9\x98Y\xd2\u05c5\xa1w\x8dH\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\ap\xb4=\xba\xe4\xb1\xf3Z\x92{O\xa8\x12M8f\xca\xf9{\x897\x19>\xa7\xef[G\x00\x00\xe0\x94\ap\xc6\x1b\xe7\x87r#\f\xb5\xa3\xbb$)\xa7&\x14\xa0\xb36\x8a\x01n\u0899\xb7\x13A\x80\x00\u07d4\ar><0\xe8\xb71\xeeEj)\x1e\xe0\u7630 Jw\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\as\xee\xac\xc0P\xf7G \xb4\xa1\xbdW\x89[\x1c\xce\xebI]\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\a\x80\r/\x80h\xe4H\u01daOi\xb1\xf1^\xf6\x82\xaa\xe5\xf6\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\a\xa8\xda\xde\xc1BW\x1a}S\xa4)pQxm\a,\xbaU\x89\x01;m\xa1\x13\x9b\u0680\x00\u07d4\a\xaf\x93\x8c\x127\xa2|\x900\tM\xcf$\aP$n=,\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\a\xb1\xa3\x06\xcbC\x12\xdffH,,\xaer\xd1\xe0a@\x0f\u034a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\a\xb7\xa5p3\xf8\xf1\x130\xe4f^\x18]#N\x83\xec\x14\v\x89\xea~\xe9*\f\x9a\v\x80\x00\u07d4\a\xbc,\xc8\xee\xdc\x01\x97\a\x00\xef\xc9\xc4\xfb6s^\x98\xcdq\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd4\x12\x17\xba\u0725\xe0\xe6\x03'\xd8E\xa3FO\x0f'\xf8J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\a\xd43N\u00c5\xe8\xaaT\xee\xda\xea\xdb0\x02/\f\u07e4\xab\x89\x8e\x91\xd5 \xf2\xeby\x00\x00\u07d4\a\xda\xe6\"c\r\x1168\x193\u04adk\"\xb89\xd8!\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\a\xdc+\xf8;\u01af\x19\xa8B\xff\xeaf\x1a\xf5\xb4\x1bg\xfd\xa1\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\a\u070c\x8b\x92z\xdb\xed\xfa\x8f]c\x9bCR5\x1f/6\u0489\x11\n\xed;U0\xdb\x00\x00\u07d4\a\xdd\xd0B,\x86\xefe\xbf\f\u007f\xc3E(b\xb1\"\x8b\b\xb8\x89o\xf5\u04aa\x8f\x9f\xcf\x00\x00\u07d4\a\xe1\x16,\xea\xe3\xcf!\xa3\xf6-\x10Y\x900.0\u007fN;\x89R\xf1\x03\xed\xb6k\xa8\x00\x00\u07d4\a\xe2\xb4\xcd\xee\xd9\u0407\xb1.Um\x9ew\f\x13\xc0\x99a_\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\a\xfe\xefT\xc16\x85\b)\xba\xdcKI\xc3\xf2\xa7<\x89\xfb\x9e\x89\x06hZ\xc1\xbf\xe3,\x00\x00\u07d4\b\x05FP\x8a=&\x82\u0239\x88O\x13c{\x88G\xb4M\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\t\bv\xba\xad\xfe\xe6\\=6;\xa5S\x12t\x8c\xfa\x87=\x89\\*\x997\x1c\xff\xe1\x00\x00\u07d4\b\x16o\x021?\xea\u12f0D\xe7\x87|\x80\x8bU\xb5\xbfX\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b)\xd0\xf7\xbb|Dl\xfb\xb0\u07ad\xb29M\x9d\xb7$\x9a\x87\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\b0m\xe5\x19\x81\u7b21\x85hY\xb7\xc7xijki\xf9\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\b7S\x9b_jR*H,\xdc\u04e9\xbbpC\xaf9\xbd\u048a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\b8\xa7v\x8d\x9c*\u028b\xa2y\xad\xfe\xe4\xb1\xf4\x91\xe3&\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\bA\x16R\xc8qq6\t\xaf\x00b\xa8\xa1(\x1b\xf1\xbb\xcf\u0649K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\bM\x102Tu\x9b4<\xb2\xb9\xc2\xd8\xff\x9e\x1a\xc5\xf1E\x96\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4\bPO\x05d?\xabY\x19\xf5\xee\xa5Y%\u05e3\xed}\x80z\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b[J\xb7]\x83b\xd9\x14C\\\xed\xee\x1d\xaa+\x1e\xe1\xa2;\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\b[\xa6_\xeb\xe2>\xef\xc2\xc8\x02fj\xb1&#\x82\xcf\u0114\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\bt\x98\xc0FFh\xf3\x11P\xf4\xd3\u013c\u0765\"\x1b\xa1\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\bw\uebabx\xd5\xc0\x0e\x83\xc3+-\x98\xfay\xadQH/\x89\x17\xd2-q\xdab&\x00\x00\u0794\b\x93j7\u07c5\xb3\xa1X\xca\xfd\x9d\xe0!\xf5\x817h\x13G\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\b\xa9\xa4N\x1fA\xde=\xbb\xa7\xa3c\xa3\xabA,\x12L\xd1^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xb7\xbd\u03d4MUp\x83\x8b\xe7\x04`$:\x86\x94HXX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xb8E6\xb7L\x8c\x01T=\xa8\x8b\x84\u05cb\xb9WG\xd8\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\b\xc2\xf26\xacJ\xdc\xd3\xfd\xa9\xfb\xc6\xe4S\"S\xf9\xda;\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\b\xc8\x02\xf8wX4\x9f\xa0>k\xc2\xe2\xfd\a\x91\x19~\ua689lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xc9\U0007fd89\xfd\xf8\x04\xd7i\xf8!#6\x02\x15\xaf\xf9;\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\b\xca\u0215&A\xd8\xfcRn\xc1\xabO-\xf8&\xa5\xe7q\x0f\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\b\xcc\xdaP\xe4\xb2j\x0f\xfc\x0e\xf9.\x92\x051\a\x06\xbe\xc2\u01ca\x01Iul8W\xc6\x00\x00\x00\u07d4\b\u0406M\xc3/\x9a\xcb6\xbfN\xa4G\xe8\xddg&\x90j\x15\x89lnY\xe6|xT\x00\x00\u07d4\b\xd4&\u007f\xeb\x15\u0697\x00\xf7\xcc\xc3\xc8J\x89\x18\xbf\x17\xcf\u0789a\t=|,m8\x00\x00\xe0\x94\b\xd41\x1c\x9c\x1b\xba\xf8\u007f\xab\xe1\xa1\xd0\x14c\x82\x8d]\x98\u038a\x13\x0e\xe8\xe7\x17\x90D@\x00\x00\u07d4\b\xd5N\x83\xadHj\x93L\xfa\xea\u20e3>\xfd\"|\x0e\x99\x898S\x05\x83$^\xdc\x00\x00\u07d4\b\xd9~\xad\xfc\xb7\xb0d\xe1\xcc\xd9\u0217\x9f\xbe\xe5\xe7z\x97\x19\x89\x0el]\xa8\xd6z\xc1\x80\x00\u07d4\b\xda:z\x0fE!a\u03fc\xec1\x1b\xb6\x8e\xbf\xde\xe1~\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\b\xe3\x8e\xe0\xceH\xc9\xcad\\\x10\x19\xf7;SUX\x1cV\xe6\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\b\xef?\xa4\xc4<\xcd\xc5{\"\xa4\xb9\xb23\x1a\x82\xe58\x18\xf2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\td\x8c\x18\xa3\xce[\xaez\x04~\xc2\xf8h\xd2L\u0768\x1d\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\t\f\xd6{`\xe8\x1dT\xe7\xb5\xf6\a\x8f>\x02\x1b\xa6[\x9a\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\f\xeb\xef),>\xb0\x81\xa0_\u062a\xf7\u04db\xf0{\x89\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\t\x0f\xa96{\xdaW\xd0\xd3%:\n\x8f\xf7l\xe0\xb8\xe1\x9as\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x14n\xa3\x88Qv\xf0w\x82\xe1\xfe0\xdc\xe3\xce$\u011e\x1f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\t!`_\x99\x16N;\xcc(\xf3\x1c\xae\xcex\x971\x82V\x1d\x89+\ai*\x90e\xa8\x00\x00\xe0\x94\t&\x1f\x9a\xcbE\x1c7\x88\x84O\f\x14Q\xa3[\xadP\x98\xe3\x8a\x01\u056d'P) `\x00\x00\xe0\x94\t'\"\x04\x92\x19K.\u069f\u013b\xe3\x8f%\u0581\xdf\xd3l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\t*\xcbbK\b\xc0U\x10\x18\x9b\xbb\xe2\x1ee$\xd6D\u032d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t.\x81UX@-g\xf9\rk\xfem\xa0\xb2\xff\xfa\x91EZ\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\tP0\xe4\xb8&\x92\xdc\xf8\xb8\u0411$\x94\xb9\xb3x\xec\x93(\x89H\xa4<T`/p\x00\x00\u07d4\tRp\xccB\x14\x1d\u0658\xad(b\xdb\xd1\xfe\x9bD\xe7\xe6P\x89A\rXj \xa4\xc0\x00\x00\u07d4\tTW\xf8\xef\x8e+\xdc6!\x96\xb9\xa9\x12]\xa0\x9cg\u3ac9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\tT\xa8\xcb]2\x1f\xc35\x1au#\xa6\x17\xd0\xf5\x8d\xa6v\xa7\x89\x87\u067cz\xa4\x98\xe8\x00\x00\xe0\x94\t[\x0e\xa2\xb2\x18\xd8.\n\xea|(\x89#\x8a9\u027f\x90w\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\t[\x94\x9d\xe33:7}P\x19\u0613uJ^FV\xff\x97\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94\t^\x01t\x82\x9f4\xc3x\x1b\xe1\xa5\xe3\x8d\x15A\xeaC\x9b\u007f\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\t_ZQ\xd0oc@\xd8\vm)\xea.\x88\x11\x8a\xd70\xfe\x89lnY\xe6|xT\x00\x00\u07d4\th\xeeZ7\x8f\x8c\xad\xb3\xba\xfd\xbe\xd1\u045a\xaa\u03d3g\x11\x8965\u026d\xc5\u07a0\x00\x00\u07d4\tw\xbf\xba\x03\x8aD\xfbI\xb09p\xd8\xd8\xcf,\xb6\x1f\x8b%\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\t}\xa1,\xfc\x1f|\x1a$d\xde\xf0\x8c)\xbe\xd5\xe2\xf8Q\xe9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\t~\u0362%g\xc2\xd9\x1c\xb0?\x8cR\x15\xc2.\x9d\u0369I\x89\x01\x16Q\xac>zu\x80\x00\u07d4\t\x89\xc2\x00D\v\x87\x89\x91\xb6\x9d`\x95\xdf\xe6\x9e3\xa2.p\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\t\x90\xe8\x1c\u05c5Y\x9e\xa26\xbd\x19f\xcfRc\x02\xc3[\x9c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\t\x98\xd8'1\x15\xb5j\xf4<P^\bz\xff\x06v\xed6Y\x89\xd8\xd6\xed\xdf-.\x18\x00\x00\u07d4\t\xa0%1o\x96\u007f\xa8\xb9\xa1\xd6\a\x00\x06?Zh\x00\x1c\xaa\x89\x02\x12!\xa9\x9b\x93\xec\x00\x00\u0794\t\xa9(\xd5(\xec\x1b>%\xff\xc8>!\x8c\x1e\n\xfe\x89(\u01c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\t\xaeI\xe3\u007f\x12\x1d\xf5\xdc\x15\x8c\xfd\xe8\x06\xf1s\xa0k\f\u007f\x89\xd80\x9e&\xab\xa1\xd0\x00\x00\u07d4\t\xaf\xa7;\xc0G\xefF\xb9w\xfd\x97c\xf8r\x86\xa6\xbeh\u0189\x1b/\xb5\xe8\xf0jf\x00\x00\u07d4\t\xb4f\x86\x96\xf8j\b\x0f\x8b\xeb\xb9\x1d\xb8\xe6\xf8p\x15\x91Z\x89#\x8f\xf7\xb3O`\x01\x00\x00\xe0\x94\t\xb5\x9b\x86\x98\xa7\xfb\xd3\xd2\xf8\xc7:\x00\x89\x88\xde>@k+\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\t\xb7\xa9\x88\xd1?\xf8\x91\x86so\x03\xfd\xf4au\xb5=\x16\xe0\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xc1w\xf1\xaeD$\x11\u076c\xf1\x87\xd4m\xb9V\x14\x83`\xe7\x8a\x01\xe5.3l\xde\"\x18\x00\x00\xe0\x94\t\u020f\x91~Mj\xd4s\xfa\x12\u93a3\xc4G*^\xd6\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\t\u0438\xcd\a|i\xd9\xf3-\x9c\xcaC\xb3\xc2\b\xa2\x1e\u050b\x89\b!\xd2!\xb5)\x1f\x80\x00\xe0\x94\t\xd6\xce\xfdu\xb0\u0133\xf8\xf1\u0587\xa5\"\xc9a#\xf1\xf59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\t\xe47\xd4H\x86\x12(\xa22\xb6.\xe8\xd3ye\xa9\x04\ud70a\x04\x98\xcf@\x1d\xf8\x84.\x80\x00\u07d4\t\xee\x12\xb1\xb4+\x05\xaf\x9c\xf2\a\xd5\xfc\xac%[.\xc4\x11\xf2\x89\x031\xcd\xddG\xe0\xfe\x80\x00\u07d4\t\xf3\xf6\x01\xf6\x05D\x11@Xl\xe0eo\xa2J\xa5\xb1\u066e\x89Sswo\xe8\xc4T\x00\x00\u07d4\t\xf9W[\xe5}\x00G\x93\u01e4\ub137\x15\x87\xf9|\xbbj\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\x06P\x86\x1fx^\xd8\xe4\xbf\x10\x05\xc4P\xbb\xd0n\xb4\x8f\xb6\x89\xa6A;y\x14N~\x00\x00\u07d4\n\x06\xfa\xd7\xdc\u05e4\x92\xcb\xc0S\xee\xab\xdei4\xb3\x9d\x867\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n\a}\xb1?\xfe\xb0\x94\x84\xc2\x17p\x9dX\x86\xb8\xbf\x9cZ\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n\x0e\u0366cow\x16\xef\x19saF\x87\xfd\x89\xa8 \xa7\x06\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\n)\xa8\xa4\xd5\xfd\x95\x00u\xff\xb3Mw\xaf\xeb-\x82;\u0589\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n*\u0795\xb2\xe8\xc6m\x8a\xe6\xf0\xbad\xcaW\u05c3\xbemD\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\n+O\xc5\xd8\x1a\xceg\xdcK\xba\x03\xf7\xb4UA=F\xfe=\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\n-\xcbzg\x17\x01\u06f8\xf4\x95r\x80\x88&Xs5l\x8e\x89\b?\x16\xce\b\xa0l\x00\x00\u07d4\n=\xe1U\xd5\xec\xd8\xe8\x1c\x1f\xf9\xbb\xf07\x83\x01\xf8\xd4\xc6#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\nG\xad\x90Y\xa2I\xfc\x93k&b5=\xa6\x90_u\u00b9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\nH)ov1p\x8c\x95\u04b7Iu\xbcJ\xb8\x8a\xc19*\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\nJ\x01\x19\x95\u0181\xbc\x99\x9f\xddyuN\x9a2J\xe3\xb3y\x8a\b\xc1\x9a\xb0n\xb8\x9a\xf6\x00\x00\u07d4\nX\xfd\xddq\x89\x8d\xe7s\xa7O\xda\xe4^{\xd8N\xf46F\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\n[y\xd8\xf2;d\x83\xdb\u2f6ab\xb1\x06L\xc7cf\xae\x89j\u0202\x10\tR\u01c0\x00\u07d4\ne.*\x8bw\xbd\x97\xa7\x90\xd0\xe9\x13a\u0248\x90\u06f0N\x8965\u026d\xc5\u07a0\x00\x00\u07d4\nn\xber;n\xd1\xf9\xa8ji\xdd\xdah\xdcGF\\+\x1b\x89@=-\xb5\x99\xd5\xe4\x00\x00\u07d4\nw\xe7\xf7+C{WO\x00\x12\x8b!\xf2\xac&Q3R\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\n\x91\u007f;\\\xb0\xb8\x83\x04\u007f\u0676Y=\xbc\xd5W\xf4S\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\n\x93\x1bD\x9e\xa8\xf1,\xdb\xd5\xe2\xc8\xccv\xba\xd2\xc2|\x069\x89\x01?\x9e\x8cy\xfe\x05\x80\x00\u0794\n\x98\x04\x13x\x03\xbahh\xd9:U\xf9\x98_\xcdT\x04Q\u4239\x8b\xc8)\xa6\xf9\x00\x00\u07d4\n\x9a\xb2c\x8b\x1c\xfdeM%\u06b0\x18\xa0\xae\xbd\u07c5\xfdU\x89\x01.\x8c\xb5\xfeLJ\x80\x00\u07d4\n\xb3f\xe6\xe7\u056b\xbc\xe6\xb4JC\x8di\xa1\u02bb\x90\xd13\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\n\xb4(\x1e\xbb1\x85\x90\xab\xb8\x9a\x81\xdf\a\xfa:\xf9\x04%\x8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\n\xb5\x9d9\a\x02\xc9\xc0Y\xdb\x14\x8e\xb4\xf3\xfc\xfa}\x04\xc7\xe7\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\n\xbf\xb3\x9b\x11HmyW(f\x19[\xa2lc\vg\x84\u06ca\x19\xba\x877\xf9i(\xf0\x00\x00\u07d4\n\u029aV&\x91;\b\xcf\u0266m@P\x8d\xceR\xb6\x0f\x87\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\n\xd3\xe4M<\x00\x1f\xa2\x90\xb3\x93ap0TA\b\xacn\xb9\x89j\xbd\xa0\xbc0\xb2\u07c0\x00\u07d4\n\xec.Bn\xd6\xcc\f\xf3\xc2I\xc1\x89~\xacG\xa7\xfa\xa9\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\n\xf6_\x14xNU\xa6\xf9Vg\xfds%*\x1c\x94\a-*\x89\nv;\x8e\x02\xd4O\x80\x00\u07d4\n\xf6\xc8\xd59\xc9mP%\x9e\x1b\xa6q\x9e\x9c\x80`\xf3\x88\u008965\u026d\xc5\u07a0\x00\x00\u07d4\v\x069\x0f$7\xb2\x0e\u0123\xd3C\x1b2y\xc6X>^\u05c9\n\x84Jt$\xd9\xc8\x00\x00\u07d4\v\v8b\x11*\xee\u00e04\x92\xb1\xb0_D\x0e\xcaT%n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\x0e\x05[(\xcb\xd0=\xc5\xffD\xaad\xf3\xdc\xe0O^c\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x11\x9d\xf9\x9ck\x8d\xe5\x8a\x1e,?)zgD\xbfU\"w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\x14\x89\x19\x99\xa6\\\x9e\xf73\b\xef\xe3\x10\f\xa1\xb2\x0e\x81\x92\x89+^:\xf1k\x18\x80\x00\x00\u07d4\v!\x13PE4d*\x1d\xaf\x10.\xee\x10\xb9\xeb\xdev\xe2a\x89\x94,\xdd|\x95\xf2\xbd\x80\x00\xe0\x94\v(\x8aZ\x8bu\xf3\xdcA\x91\xeb\x04W\xe1\xc8=\xbd M%\x8a\x01\a\x14\xe7{\xb4:\xb4\x00\x00\u07d4\v6\x9e\x00.\x1bLy\x13\xfc\xf0\x0f-^\x19\u0141eG\x8f\x89\x03\u007fe\x16(\x8c4\x00\x00\u07d4\vC\xbd#\x91\x02U\x81\u0615l\xe4*\a%y\u02ff\xcb\x14\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\vP|\xf5SV\x8d\xaa\xf6U\x04\xaeN\xaa\x17\xa8\xea<\xdb\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v]f\xb1<\x87\xb3\x92\xe9M\x91\xd5\xf7l\rE\nU(C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v^ \x11\xeb\xc2Z\x00\u007f!6)`I\x8a\xfb\x8a\xf2\x80\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vd\x9d\xa3\xb9j\x10,\xdcm\xb6R\xa0\xc0}e\xb1\xe4C\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vi \xa6K6;\x8d]\x90\x80$\x94\xcfVKT|C\r\x89A\rXj \xa4\xc0\x00\x00\u07d4\vp\x11\x01\xa4\x10\x9f\x9c\xb3`\xdcW\xb7tBg=^Y\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\vq\xf5T\x12$i\uf5ce/\x1f\xef\xd7\u02f4\x10\x98'r\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\xe0\x94\v{\xb3B\xf0\x1b\u0248\x8ej\x9a\xf4\xa8\x87\xcb\xf4\xc2\xdd,\xaf\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\v}3\x93q\xe5\xbeg'\xe6\xe31\xb5\x82\x1f\xa2K\u06ddZ\x89.\u007f\x81\x86\x82b\x01\x00\x00\u07d4\v\u007f\xc9\xdd\xf7\x05v\xf63\x06i\xea\xaaq\xb6\xa81\xe9\x95(\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\v\x80\xfcp(,\xbd\xd5\xfd\xe3[\xf7\x89\x84\xdb;\xdb\x12\x01\x88\x8968\x02\x1c\xec\u06b0\x00\x00\u07d4\v\x92M\xf0\a\xe9\xc0\x87\x84\x17\xcf\xe6;\x97n\xa1\xa3\x82\xa8\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\v\x93\xfc\xa4\xa4\xf0\x9c\xac \xdb`\xe0e\xed\xcc\xcc\x11\u0976\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\v\x9d\xf8\x0f\xbe# \t\xda\xcf\n\xa8\xca\u0153v\xe2Gb\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xa6\xe4j\xf2Z\x13\xf5qi%Z4\xa4\xda\xc7\xce\x12\xbe\x04\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\v\xa8p[\xf5\\\xf2\x19\xc0\x95k^?\xc0\x1cDt\xa6\xcd\xc1\x89\x05%\xe0Y]Mk\x80\x00\u07d4\v\xafn\u0379\x1a\xcb6\x06\xa85|\v\xc4\xf4\\\xfd-~o\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xb0_r$\xbbX\x04\x85eV\xc0~\xea\xdb\ud1fa\x8f|\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\v\xb0\xc1&\x82\xa2\xf1\\\x9bWA\xb28\\\xbeA\xf04\x06\x8e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\v\xb2\\\xa7\u0448\xe7\x1eMi={\x17\a\x17\xd6\xf8\xf0\xa7\n\x89\x12C\x02\xa8/\xad\xd7\x00\x00\u07d4\v\xb2e\x0e\xa0\x1a\xcau[\xc0\xc0\x17\xb6K\x1a\xb5\xa6m\x82\xe3\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb5Lr\xfdf\x10\xbf\xa463\x97\xe0 8K\x02+\fI\x89Hz\x9a0E9D\x00\x00\u07d4\v\xb7\x16\n\xba)7b\xf8sO>\x03&\xff\u0264\xca\xc1\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4\v\xc9\\\xb3-\xbbWL\x83/\xa8\x17J\x815m8\xbc\x92\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xd6}\xbd\xe0z\x85n\xbd\x89;^\xdcO:[\xe4 &\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xdb\xc5L\u023d\xbb\xb4\x02\xa0\x89\x11\xe2#*T`\u0386k\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\v\xddX\xb9n|\x91m\xd2\xfb05o*\xeb\xfa\xaf\x1d\x860\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\v\u1f39\x03C\xfa\xe501s\xf4a\xbd\x91JH9\x05l\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\v\xe1\xfd\xf6&\xeea\x89\x10-p\xd1;1\x01,\x95\xcd\x1c\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\v\xe2\xb9J\xd9P\xa2\xa6&@\xc3[\xfc\xcdlg\xda\xe4P\xf6\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\v\u681eC\a\xfeH\xd4\x12\xb8\u0461\xa8(M\xceHba\x8a\x04\x0f\xbf\xf8\\\x0180\x00\x00\u07d4\v\xef\xb5G\a\xf6\x1b,\x9f\xb0G\x15\xab\x02n\x1b\xb7 B\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\v\xf0dB\x8f\x83bg\"\xa7\xb5\xb2j\x9a\xb2\x04!\xa7r>\x89\a?u\u0460\x85\xba\x00\x00\u07d4\v\xfb\xb6\x92]\xc7^R\xcf&\x84\"K\xbe\x05P\xfe\xa6\x85\u04c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\f\b\x80\x06\xc6K0\xc4\u076f\xbc6\xcb_\x05F\x9e\xb6(4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f s\xbaD\xd3\u077d\xb69\xc0N\x19\x109\xa7\x17\x16#\u007f\x89M\x85<\x8f\x89\b\x98\x00\x00\xe0\x94\f\",|A\u0270H\xef\xcc\xe0\xa22CCb\xe1-g;\x8a\x02\x1e\x83Yivw8\x00\x00\xe0\x94\f(\b\xb9Q\ud787-{2y\x0f\xccY\x94\xaeA\xff\u070a\x15\x99n[<\u05b3\xc0\x00\x00\u07d4\f(\x84~O\t\xdf\xce_\x9b%\xaf|NS\x0fY\u0200\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f-\\\x92\x058\xe9S\u02af$\xf0s\u007fUL\u0192wB\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f0\xca\xcc?r&\x9f\x8bO\x04\xcf\a=+\x05\xa8=\x9a\u0449lyt\x12?d\xa4\x00\x00\u07d4\f29\xe2\xe8A$-\xb9\x89\xa6\x15\x18\xc2\"G\xe8\xc5R\b\x89\x0eJ\xf6G\x174d\x00\x00\xe0\x94\fH\r\xe9\xf7F\x10\x02\x90\x8bI\xf6\x0f\xc6\x1e+b\xd3\x14\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\fH\xaeb\xd1S\x97\x88\xeb\xa0\x13\xd7^\xa6\vd\xee\xbaN\x80\x89w\xfb\xdcC\xe00\x99\x80\x00\u07d4\fU\x89\xa7\xa8\x9b\x9a\xd1[\x02u\x190AYH\xa8u\xfb\xef\x89\x06\u0519\xeclc8\x00\x00\u07d4\fg\x03=\xd8\xee\u007f\f\x8a\xe54\xd4*Q\xf7\xd9\xd4\xf7\x97\x8f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\fhE\xbfA\xd5\xee'<>\u6d70\u059fo\xd5\xea\xbb\xf7\x89\xa2\xa1\xb9h.X\t\x00\x00\xe0\x94\f\u007f\x86\x9f\x8e\x90\xd5?\xdc\x03\u8c81\x9b\x01k\x9d\x18\xeb&\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\f\x86\x92\xee\xff*S\xd6\xd1h\x8e\xd5j\x9d\u06fdh\u06bb\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\x8ff\xc6\x01{\xce[ 4r\x04\xb6\x02\xb7C\xba\u05cd`\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\f\x8f\xd7w^T\xa6\xd9\u0263\xbf\x89\x0ev\x1fewi?\xf0\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\f\x92Z\xd5\xeb5,\x8e\xf7m\f\"-\x11[\a\x91\xb9b\xa1\x89\xacc]\u007f\xa3N0\x00\x00\u07d4\f\x96~0a\xb8zu>\x84P~\xb6\t\x86x,\x8f0\x13\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\f\xa1*\xb0\xb9fl\xf0\xce\xc6g\x1a\x15)/&SGj\xb2\x8a,x'\xc4-\"\xd0|\x00\x00\u07d4\f\xa6p\xeb,\x8b\x96\u02e3y!\u007fY)\u00b8\x92\xf3\x9e\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\f\xae\x10\x8em\xb9\x9b\x9ecxv\xb0d\xc60>\u068ae\u0209\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\f\xbd\x92\x1d\xbe\x12\x15c\xb9\x8ahq\xfe\xcb\x14\xf1\xcc~\x88\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\f\xbf\x87p\xf0\xd1\b.\\ \u016e\xad4\xe5\xfc\xa9\xaez\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\f\xc6\u007f\x82s\xe1\xba\xe0\x86\u007f\xd4.\x8b\x81\x93\xd7&y\xdb\xf8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\f\u05a1A\x91\x8d\x12k\x10m\x9f.\xbfi\xe1\x02\xdeM2w\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\f\xda\x12\xbfr\xd4a\xbb\xc4y\xeb\x92\xe6I\x1d\x05~kZ\u044a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\f\u0716\v\x99\x8c\x14\x19\x98\x16\r\xc1y\xb3l\x15\u0484p\xed\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\f\xfb\x17#5\xb1l\x87\xd5\x19\xcd\x14uS\r W\u007f^\x0e\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\r\x1f*Wq>\xbcn\x94\xde)\x84n\x88D\xd3vfWc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\r2e\xd3\u7f79=^\x8e\x8b\x1c\xa4\u007f!\ny>\u030e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r5@\x8f\"ef\x11o\xb8\xac\u06a9\xe2\xc9\u055bvh?\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\rU\x1e\xc1\xa2\x13<\x98\x1d_\u01a8\xc8\x17?\x9e|OG\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r]\x98V\\d|\xa5\xf1w\xa2\xad\xb9\xd3\x02/\xac(\u007f!\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\re\x80\x14\xa1\x99\x06\x1c\xf6\xb3\x943\x14\x03\x03\xc2\x0f\xfdNZ\x8a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\rg\x87\x06\xd07\x18\u007f>\"\xe6\xf6\x9b\x99\xa5\x92\xd1\x1e\xbcY\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\ri\x10\f9\\\xe6\xc5\xea\xad\xf9]\x05\xd8r\x83~\xde\xdd!\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\rt~\u559b\xf7\x9dW8\x1do\xe3\xa2@l\xd0\xd8\xce'\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\r\x80#\x92\x9d\x91r4\xae@Q+\x1a\xab\xb5\xe8\xa4Q'q\x89\b\x05\xe9\x9f\xdc\xc5\xd0\x00\x00\xe0\x94\r\x8a\xab\x8ft\xea\x86,\xdfvh\x05\x00\x9d?>B\xd8\xd0\v\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\r\x8c@\xa7\x9e\x18\x99O\xf9\x9e\xc2Q\xee\x10\u0408\u00d1.\x80\x89\x066d\xfc\u04bb\xc4\x00\x00\u07d4\r\x8e\xd7\xd0\xd1V83\x0e\xd7\xe4\xea\u032b\x8aE\x8dus~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\x92X/\u06e0^\xab\xc3\xe5\x158\xc5m\xb8\x817\x85\xb3(\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\r\x94C\xa7\x94h\xa5\xbb\xf7\xc1<n\"]\x1d\xe9\x1a\xee\a\u07c9\x03\xcbq\xf5\x1f\xc5X\x00\x00\xe0\x94\r\x9a\x82_\xf2\xbc\u04d7\u02ed[q\x1d\x9d\u0315\xf1\xcc\x11-\x8a\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\x00\u07d4\r\x9d?\x9b\u0124\xc6\xef\xbdYg\x9bi\x82k\xc1\xf6=\x99\x16\x89 \x86\xac5\x10R`\x00\x00\u07d4\r\xa52\xc9\x10\xe3\xac\r\xfb\x14\xdba\xcds\x9a\x935?\xd0_\x89Hx\xbe\x1f\xfa\xf9]\x00\x00\u07d4\r\xa7@\x12b8N.\x8bK&\xdd\x15G\x99\xb5QE\uf809\x10CV\x1a\x88)0\x00\x00\u07d4\r\xae>\xe5\xb9\x15\xb3d\x87\xf9\x16\x1f\x19\x84m\x10\x1431\x8a\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\r\xbdA|7+\x8b\r\x01\xbc\xd9Dpk\xd3.`\xae(\u0449\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\r\xc1\x00\xb1\a\x01\x1c\u007f\xc0\xa13\x96\x12\xa1l\xce\xc3(R\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\r\u03dd\x8c\x98\x04E\x9fd|\x14\x13\x8e\xd5\x0f\xadV;AT\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\r\xcf\xe87\xea\x1c\xf2\x8ce\xfc\xce\u00fe\xf1\xf8NY\xd1P\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\r\xd4\xe6t\xbb\xad\xb1\xb0\u0702D\x98q=\xce;QV\xda)\x89\t79SM(h\x00\x00\u07d4\r\xfb\u0501pP\xd9\x1d\x9db\\\x02\x05<\xf6\x1a>\xe2\x85r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x0e\x02N\u007f\x02\x9cj\xaf:\x8b\x91\x0f^\b\bs\xb8W\x95\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\tdl\x99\xafC\x8e\x99\xfa'L\xb2\xf9\xc8V\xcbe\xf76\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x0e\f\x9d\x00^\xa0\x16\u0095\xcdy\\\xc9!>\x87\xfe\xbc3\xeb\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\x0e\rf3\xdb\x1e\f\u007f#Jm\xf1c\xa1\x0e\n\xb3\x9c \x0f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0e\x11\xd7z\x89w\xfa\xc3\r&\x84E\xe51\x14\x9b1T\x1a$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x12=}\xa6\xd1\xe6\xfa\xc2\u072d\xd2p)$\v\xb3\x90R\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0e\x18\x01\xe7\vbb\x86\x1b\x114\u033c9\x1fV\x8a\xfc\x92\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e \x94\xac\x16T\xa4k\xa1\xc4\u04e4\v\xb8\xc1}\xa7\U000d6209\x13h?\u007f<\x15\xd8\x00\x00\u07d4\x0e!\xaf\x1b\x8d\xbf'\xfc\xf6?7\xe0G\xb8z\x82\\\xbe|'\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x0e.PJ-\x11\"\xb5\xa9\xfe\xee\\\xb1E\x1b\xf4\u00ac\xe8{\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x0e/\x8e(\xa6\x81\xf7|X;\xd0\xec\xde\x16cK\xdd~\x00\u0349\x05'8\xf6Y\xbc\xa2\x00\x00\u07d4\x0e2\x02\x19\x83\x8e\x85\x9b/\x9f\x18\xb7.=@s\xcaP\xb3}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e3\xfc\xbb\xc0\x03Q\v\xe3W\x85\xb5*\x9c]!k\xc0\x05\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x0e6\x96\xcf\x1fB\x17\xb1c\u047c\x12\xa5\xeas\x0f\x1c2\xa1J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0e9\x0fD\x05=\xdf\xce\xf0\xd6\b\xb3^M\x9c,\xbe\x98q\xbb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x0e:(\xc1\u07ef\xb0P[\xdc\xe1\x9f\xe0%\xf5\x06\xa6\xd0\x1c\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e=\xd7\xd4\xe4)\xfe90\xa6A@5\xf5+\xdcY\x9dxM\x89\x02,\xa3X|\xf4\xeb\x00\x00\u07d4\x0eGey\x03Rek\xc6Vh,$\xfc^\xf3\xe7j#\u01c9\x02\x86\xd7\xfc\f\xb4\xf5\x00\x00\u07d4\x0eI\x88\x00Dqw\xb8\u022f\xc3\xfd\xfa\u007fi\xf4\x05\x1b\xb6)\x89t\x05\xb6\x9b\x8d\xe5a\x00\x00\u07d4\x0ek\xaa\xa3\u07b9\x89\xf2\x89b\x00vf\x86\x18\xe9\xac3(e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x0el\xd6d\xad\x9c\x1e\xd6K\xf9\x87I\xf4\x06D\xb6&\xe3y,\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\xe0\x94\x0em\xfdU;.\x87=*\xec\x15\xbd_\xbb?\x84r\xd8\u04d4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\x0en\xc3\x137bq\xdf\xf5T#\xabT\"\xcc:\x8b\x06\xb2+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x0en\u0399\x11\x1c\xad\x19a\xc7H\xed=\xf5\x1e\xddi\u04a3\xb1\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x0e\x83\xb8PH\x1a\xb4MI\xe0\xa2)\xa2\xe4d\x90,iS\x9b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\x89\xed\xdd?\xa0\xd7\x1d\x8a\xb0\xff\x8d\xa5X\x06\x86\xe3\xd4\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\x90\x96\xd3C\xc0`\xdbX\x1a\x12\x01\x12\xb2x`~\xc6\xe5+\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x0e\x9cQ\x18d\xa1w\xf4\x9b\xe7\x82\x02w?`H\x9f\xe0NR\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x0e\xa2\xa2\x101+>\x86~\xe0\xd1\xcch,\xe1\xd6f\xf1\x8e\u054a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0e\xb1\x89\xef,-Wb\xa9c\u05b7\xbd\xf9i\x8e\xa8\u7d0a\x89Hz\x9a0E9D\x00\x00\xe0\x94\x0e\xb5\xb6b\xa1\xc7\x18`\x8f\xd5/\f%\xf97\x880\x17\x85\x19\x8a\x01J7(\x1aa.t\x00\x00\xe0\x94\x0e\xc4f\x96\xff\xac\x1fX\x00_\xa8C\x98$\xf0\x8e\xed\x1d\xf8\x9b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x0e\xc5\n\xa8#\xf4e\xb9FK\v\xc0\u0125w$\xa5U\xf5\u058a\f\x83\xd1Bj\u01f1\xf0\x00\x00\u07d4\x0e\xc50\x8b1(.!\x8f\xc9\xe7Y\xd4\xfe\xc5\xdb7\b\xce\u01096C\xaady\x86\x04\x00\x00\u07d4\x0e\xcc\xf6\x17\x84O\xd6\x1f\xbab\xcb\x0eD[z\u018b\xcc\x1f\xbe\x89\x14\xfeO\xe65e\xc6\x00\x00\u07d4\x0e\u04fb:N\xb5T\xcf\u0297\x94}WU\a\xcd\xfdm!\u0609\x1d\xb3 _\xcc#\u0540\x00\u07d4\x0e\xd7l,;]P\xff\x8f\xb5\v>\xea\xcdh\x15\x90\xbe\x1c-\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0e\u0680\xf4\xed\aJ\xeaiz\xed\xdf(;c\xdb\xca=\xc4\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\x0e\xddKX\x0f\xf1\x0f\xe0lJ\x03\x11b9\xef\x96b+\xae5\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x0e\xe3\x91\xf0<v[\x11\u0590&\xfd\x1a\xb3S\x95\xdc8\x02\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0e\xe4\x14\x94\x04\x87\xfd$\xe3\x907\x82\x85\xc5\u05f93M\x8be\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\x0e\xf5J\xc7&M\"T\xab\xbb_\x8bA\xad\u0787QW\xdb|\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x0e\xf8[I\u040au\x19\x86\x92\x91N\u0774\xb2,\xf5\xfaDP\x89l\xae0b\x1dG \x00\x00\u07d4\x0e\xfd\x17\x89\xeb\x12D\xa3\xde\xde\x0f]\xe5\x82\u0616<\xb1\xf3\x9f\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94\x0f\x04,\x9c/\xb1\x87f\xf86\xbbY\xf75\xf2}\xc3)\xfe<\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0f\x04\x9a\x8b\xdf\xd7a\u078e\xc0,\xee()\xc4\x00[#\xc0k\x89\r\xa93\xd8\xd8\xc6p\x00\x00\xe0\x94\x0f\x05\xf1 \u021e\x9f\xbc\x93\u052b\f^+J\r\xf0\x92\xb4$\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x0f\x12{\xbf\x8e1\x1c\xae\xa2\xbaP*3\xfe\xce\xd3\xf70\xbaB\x89\n1\x06+\xee\xedp\x00\x00\u07d4\x0f\x1c$\x9c\xd9b\xb0\x0f\xd1\x14\xa94\x9fjl\xc7x\xd7lM\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f n\x1a\x1d\xa7 ~\xa5\x18\xb1\x12A\x8b\xaa\x8b\x06&\x03(\x89 \x86\xac5\x10R`\x00\x00\u07d4\x0f$\x10Z\xbb\u06a0?\xa60\x9e\xf6\xc1\x88\xe5\x1fqJnY\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0f&H\n\x15\ta\xb8\xe3\aPq:\x94\xeeo.G\xfc\x00\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x0f-\x8d\xaf\x04\xb5AJ\x02a\xf5I\xffdw\xb8\x0f/\x1d\a\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\x0f/\xb8\x84\u022a\xffoT:\xc6\"\x8b\u040eO`\xb0\xa5\xfd\x89\xaa}\xa4\x85\x13k\x84\x00\x00\u07d4\x0f2\xd9\xcbM\x0f\u06a0\x15\x06V\xbb`\x8d\xccC\xed}\x93\x01\x89(\u07cb\xf4@\xdby\x00\x00\u07d4\x0f6e\u050e\x9f\x14\x19\u0358O\xc7\xfa\x92x\x87\x10\xc8\xf2\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f:\x10#\xca\xc0M\xbfD\xf5\xa5\xfaj\x9c\xf8P\x8c\xd4\xfd\u07c9b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\x0f@s\xc1\xb9\x9d\xf6\n\x15I\u0597\x89\xc71\x8d\x94\x03\xa8\x14\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x0fF\xc8\x1d\xb7\x80\xc1gJ\xc7=1O\x06S\x9e\xe5n\xbc\x83\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\x0fO\x94\xb9\x19\x1b\xb7\xbbUj\xaa\xd7\xc7M\xdb(\x84\x17\xa5\v\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x0f`\x00\xde\x15xa\x93 \xab\xa5\xe3\x92pk\x13\x1f\xb1\xdeo\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4\x0fn\x84\n?*$d}\x8eC\xe0\x9dE\xc7\xc35\xdfBH\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\x0fu\x15\xff\x0e\x80\x8fi^\f H_\xf9n\xd2\xf7\xb7\x93\x10\x8968\"\x16`\xa5\xaa\x80\x00\u07d4\x0fx\x9e09|S\xbf%o\xc3d\xe6\xef9\xf8SPA\x14\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\x0f{a\u015b\x01c\"\xe8\"l\xaf\xae\xe9\xd9\xe7mP\xa1\xb3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x0f{\xeaN\xf3\xf7:\xe0#=\xf1\xe1\x00q\x8c\xbe)1\v\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f{\xf67?w\x1aF\x01v,M\xae_\xbb\xf4\xfe\u075c\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\x83*\x93\u07dd\u007ft\xcd\x0f\xb8Tkq\x98\xbfSw\xd9%\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\x0f\x83F\x1b\xa2$\xbb\x1e\x8f\u075d\xaeSQr\xb75\xac\xb4\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x0f\x85\xe4+\x1d\xf3!\xa4\xb3\xe85\xb5\f\x00\xb0as\x96\x846\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\x0f\x88\xaa\xc94l\xb0\xe74\u007f\xbap\x90Tu\xba\x8b>^\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x0f\x92\x9c\xf8\x95\xdb\x01z\xf7\x9f>\xad\"\x16\xb1\xbdi\xc3}\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa0\x10\xce\fs\x1d;b\x8e6\xb9\x1fW\x13\x00\u477e\xab\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\x0f\xa5\xd8\u0173\xf2\x94\xef\u0515\xabi\xd7h\xf8\x18rP\x85H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x0f\xa6\u01f0\x97=\v\xae)@T\x0e$}6'\xe3|\xa3G\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x0f\xad\x05P|\u070f$\xb2\xbeL\xb7\xfa]\x92}\u06d1\x1b\x88\x89\xa2\xdf\x13\xf4A\xf0\t\x80\x00\u07d4\x0f\xb5\xd2\xc6s\xbf\xb1\xdd\xca\x14\x1b\x98\x94\xfdm?\x05\xdag \x89\x05k\xc7^-c\x10\x00\x00\u07d4\x0f\u0260\xe3AE\xfb\xfd\xd2\xc9\u04a4\x99\xb6\x17\u05e0)i\xb9\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94\x0f\xcf\xc4\x06P\b\xcf\xd3#0_b\x86\xb5zM\xd7\xee\xe2;\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x0f\xdde@#\x95\u07db\u045f\xeeE\a\xefSE\xf7E\x10L\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x0f\xecN\xe0\xd7\xca\x18\x02\x90\xb6\xbd \xf9\x99#B\xf6\x0f\xf6\x8d\x89\x12 \u007f\x0e\xdc\xe9q\x80\x00\u07d4\x0f\ue06c3\x1e\xfd\x8f\x81\x16\x1cW8+\xb4P{\xb9\xeb\xec\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\u07d4\x0f\xfe\xa0mq\x13\xfbj\xec(i\xf4\xa9\u07f0\x90\a\xfa\xce\xf4\x89\f8F\x81\xb1\xe1t\x00\x00\u07d4\x10\tq\x98\xb4\xe7\xee\x91\xff\x82\xcc/;\xd9_\xeds\xc5@\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x10\vM\tw\xfc\xba\xd4\u07bd^d\xa0Iz\xea\xe5\x16\x8f\xab\x89\x11\f\x90s\xb5$Z\x00\x00\xe0\x94\x10\x1a\nd\xf9\xaf\xccD\x8a\x8a\x13\rM\xfc\xbe\xe8\x957\xd8T\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\u07d4\x10,G}i\xaa\u06e9\xa0\xb0\xf6+tY\xe1\u007f\xbb\x1c\x15a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x101\xe0\xec\xb5I\x85\xae!\xaf\x17\x93\x95\r\xc8\x11\x88\x8f\xde|\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x104d\x14\xbe\xc6\xd3\xdc\xc4NP\xe5MT\u00b8\xc3sN>\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x108\x98X\xb8\x00\xe8\xc0\xec2\xf5\x1e\xd6\x1a5YF\xcc@\x9b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x10Y\xcb\xc6>6\xc4>\x88\xf3\x00\b\xac\xa7\xce\x05\x8e\ua816\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\x10n\xd5\xc7\x19\xb5&\x14w\x89\x04%\xaeuQ\xdcY\xbd%\\\x8a\x02\x89jX\xc9[\xe5\x88\x00\x00\u07d4\x10q\x1c=\xda21x\x85\xf0\xa2\xfd\x8a\xe9.\x82\x06\x9b\r\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x10sy\xd4\xc4gFO#[\xc1\x8eU\x93\x8a\xad>h\x8a\u05c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x10v!-Ou\x8c\x8e\xc7\x12\x1c\x1c}t%I&E\x92\x84\x8a\ai[Y\xb5\xc1{L\x00\x00\u07d4\x10x\xd7\xf6\x1b\x0eV\xc7N\xe6c[.\x18\x19\xef\x1e=\x87\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x10z\x03\xcf\bB\xdb\u07b0a\x8f\xb5\x87\xcai\x18\x9e\xc9/\xf5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x10\x80\xc1\xd85\x8a\x15\xbc\x84\xda\xc8%<h\x831\x90 \xdf,\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\x10\x8a+|3oxGy\u0635M\x02\xa8\xd3\x1d\x9a\x13\x9c\n\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x10\x8b\xa7\u0089\\P\xe0r\xdco\x96I2\xd5\f(-04\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x10\x8f\xe8\xee*\x13\xdaH{\"\u01abmX.\xa7\x10d\u064c\x89\x15\xacV\xed\xc4\xd1,\x00\x00\xe0\x94\x10\x91\x17k\u16d9d\xa8\xf7.\x0e\xcek\xf8\xe3\u03edn\x9c\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\u07d4\x10\x98\xc7t\xc2\f\xa1\u06ac]\xdbb\x03e1m5?\x10\x9c\x89\x05k\xc7^-c\x10\x00\x00\u0794\x10\x98\xcc \uf13a\xd5\x14f9\xc4\xcd\x1c\xa6\u00d9l\xb9\x9b\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x10\xa1\xc4-\xc1\xbati\x86\xb9\x85\xa5\"\xa7<\x93\xea\xe6Lc\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x10\xa94WIo\x11\b\u0358\xe1@\xa1\xec\u06ee^m\xe1q\x89\x15\xa9\x90b\xd4\x16\x18\x00\x00\u07d4\x10\xb5\xb3M\x12H\xfc\xf0\x17\xf8\xc8\xff\xc4\b\u0389\x9c\xee\xf9/\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\x10\xcfV\td\xff\x83\xc1\xc9gLx<\x0fs\xfc\u0619C\xfc\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x10\xd3$\x16r,\xa4\xe6Hc\x05H\xea\xd9\x1e\xddy\xc0j\xff\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x10\xd9E3N\xcd\xe4{\ub723\x81l\x17=\xfb\xbd\vS3\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x10\xdfh\x15\x06\xe3I0\xacz\\g\xa5L>\x89\u0392\xb9\x81\x89t\xc1\xfa\xb8\xad\xb4T\x00\x00\u07d4\x10\xe1\xe37x\x85\xc4-}\xf2\x18R.\xe7vh\x87\xc0^j\x89\x10C\xc4<\xde\x1d9\x80\x00\u07d4\x10\u342d+\xa3=\x82\xb3s\x88\u041cED\u01b0\"]\xe5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x10\xf4\xbf\xf0\u02a5\x02|\nj-\xcf\xc9R\x82M\xe2\x94\t\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\x00\x1b\x89\xed\x87>:\xae\xc1\x15V4\xb4h\x16C\x98c#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x11\x027\u03d1\x17\xe7g\x92/\u0121\xb7\x8dyd\u0682\xdf \x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x11\x11\xe5\xdb\xf4^o\x90mb\x86o\x17\b\x10\x17\x88\xdd\xd5q\x89F{\xe6S>\xc2\xe4\x00\x00\xe0\x94\x11\x17+'\x8d\xddD\xee\xa2\xfd\xf4\xcb\x1d\x16\x96#\x91\xc4S\u064a\xc6/=\x9b\xfdH\x95\xf0\x00\x00\u07d4\x11&4\xb4\xec0\xffxn\x02AY\xf7\x96\xa5y9\xea\x14N\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x110l}WX\x867x\x0f\xc9\xfd\xe8\xe9\x8e\xcb\x00\x8f\x01d\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x116\x12\xbc;\xa0\xeeH\x98\xb4\x9d\xd2\x023\x90_/E\x8fb\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x11A_\xaba\xe0\xdf\u0539\x06v\x14\x1aUz\x86\x9b\xa0\xbd\xe9\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x11L\xbb\xbfo\xb5*\xc4\x14\xbe~\xc6\x1f{\xb7\x14\x95\xce\x1d\xfa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x11L\xfe\xfeP\x17\r\xd9z\xe0\x8f\nDTIx\u0159T\x8d\x89.\u0207\xe7\xa1J\x1c\x00\x00\u07d4\x11a\b\xc1 \x84a.\xed\xa7\xa9=\xdc\xf8\xd2`.'\x9e\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11d\u02aa\x8c\u0157z\xfe\x1f\xad\x8a}`(\xce-W)\x9b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x11gZ%UF\a\xa3\xb6\xc9*\x9e\xe8\xf3ou\xed\xd3\xe36\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x11j\t\xdff\xcb\x15\x0e\x97W\x8e)\u007f\xb0n\x13\x04\f\x89<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11o\xef^`\x16B\xc9\x18\u02c9\x16\x0f\xc2);\xa7\x1d\xa96\x89+|\xc2\xe9\xc3\"\\\x00\x00\u07d4\x11xP\x1f\xf9J\xdd\x1cX\x81\xfe\x88a6\xf6\xdf\xdb\xe6\x1a\x94\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x11y\xc6\r\xbd\x06\x8b\x15\v\aM\xa4\xbe#\x03; \u0185X\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\x11}\x9a\xa3\xc4\xd1;\xee\x12\xc7P\x0f\t\xf5\xdd\x1cf\xc4e\x04\x89\v*\xd3\x04\x90\xb2x\x00\x00\xe0\x94\x11}\xb867\u007f\xe1TU\xe0,.\xbd\xa4\v\x1c\xebU\x1b\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x11\x8c\x18\xb2\xdc\xe1p\xe8\xf4Eu;\xa5\xd7Q<\xb7cm-\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\x11\x8f\xbdu;\x97\x929Z\xefzMx\xd2c\xcd\u02ab\xd4\xf7\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94\x11\x92\x83x\xd2}U\xc5 \xce\xed\xf2L\xeb\x1e\x82-\x89\r\xf0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x11\x9a\xa6M[}\x18\x1d\xae\x9d<\xb4I\x95\\\x89\xc1\xf9c\xfa\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\x11\xc05\x8a\xa6G\x9d\xe2\x18f\xfe!\a\x19$\xb6^p\xf8\xb9\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\xe0\x94\x11\xd2$z\"\x1ep\xc2\xd6m\x17\xee\x13\x8d8\xc5_\xfb\x86@\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x11\u05c4JG\x1e\xf8\x9a\x8d\x87uUX<\xee\xbd\x149\xea&\x8a\x02#i\u6e80\u0188\x00\x00\u07d4\x11\xdda\x85\u0668\xd7=\xdf\u06a7\x1e\x9bwtC\x1cM\xfe\u008965\u026d\xc5\u07a0\x00\x00\u07d4\x11\xe7\x99~\u0750E\x03\xd7}\xa6\x03\x8a\xb0\xa4\xc84\xbb\xd5c\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x11\xec\x00\xf8I\xb61\x9c\xf5\x1a\xa8\u074ff\xb3U)\xc0\xbew\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x11\ufe22\x04Q\x16\x1bdJ\x8c\u03bb\xc1\xd3C\xa3\xbb\xcbR\x89\xadx\xeb\u016cb\x00\x00\x00\xe0\x94\x11\xfe\xfb]\xc1\xa4Y\x8a\xa7\x12d\fQwu\u07e1\xd9\x1f\x8c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x12\x0f\x9d\xe6\xe0\xaf~\xc0*\a\xc6\t\u0284G\xf1W\xe64L\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x12\x10\xf8\v\u06c2l\x17Tb\xab\a\x16\xe6\x9eF\xc2J\xd0v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12\x13N\u007fk\x01{\xf4\x8e\x85Z9\x9c\xa5\x8e.\x89/\xa5\u020965\u026d\xc5\u07a0\x00\x00\u07d4\x12\x170t\x98\x01S\xae\xaaK\r\xcb\xc7\x13.\xad\xce\xc2\x1bd\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\x12\x1f\x85[p\x14\x9a\xc84s\xb9po\xb4MG\x82\x8b\x98;\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x12'\xe1\nM\xbf\x9c\xac\xa3\x1b\x17\x80#\x9fUv\x15\xfc5\xc1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x12-\xcf\xd8\x1a\u0779}\x1a\x0eI%\u0135I\x80n\x9f;\xeb\x89R 5\xccn\x01!\x00\x00\u07d4\x12/V\x12%I\xd1h\xa5\xc5\xe2g\xf5&b\xe5\xc5\xcc\xe5\u0209\n\ad\a\xd3\xf7D\x00\x00\xe0\x94\x121o\xc7\xf1x\xea\xc2.\xb2\xb2Z\xed\xea\xdf=u\xd0\x01w\x8a\x04<3\xbe\x05\xf6\xbf\xb9\x80\x00\xe0\x94\x127Y\xf33\xe1>0i\xe2\x03KO\x059\x89\x18\x11\x9d6\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x12\\\xc5\xe4\xd5k+\xcc.\xe1\xc7\t\xfb\x9eh\xfb\x17t@\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x12c#\x88\xb2v^\xe4E+P\x16\x1d\x1f\xff\xd9\x1a\xb8\x1fJ\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\x12h\x97\xa3\x11\xa1J\xd4;x\xe0\x92\x01\x00\xc4Bk\xfdk\u07494\xc7&\x89?-\x94\x80\x00\u07d4\x12m\x91\xf7\xad\x86\u07bb\x05W\xc6\x12\xca'n\xb7\xf9m\x00\xa1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x12}?\xc5\x00;\xf6<\r\x83\xe99W\x83e\x15\xfd'\x90E\x89\x06\x10\xc9\".nu\x00\x00\xe0\x94\x12}\xb1\xca\xdf\x1bw\x1c\xbdtu\xe1\xb2ri\x0fU\x8c\x85e\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x12\x84\xf0\xce\xe9\xd2\xff)\x89\xb6Ut\xd0o\xfd\x9a\xb0\xf7\xb8\x05\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x12\x8b\x90\x8f\xe7C\xa44 =\xe2\x94\xc4A\xc7\xe2\n\x86\xeag\x89&\xab\x14\xe0\xc0\xe1<\x00\x00\xe0\x94\x12\x93\u01cc}jD;\x9dt\xb0\xba^\xe7\xbbG\xfdA\x85\x88\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x12\x96\xac\xde\xd1\xe0c\xaf9\xfe\x8b\xa0\xb4\xb6=\xf7\x89\xf7\x05\x17\x89\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\x12\xaa}\x86\xdd\xfb\xad0\x16\x92\xfe\xac\x8a\b\xf8A\xcb!\\7\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94\x12\xaf\xbc\xba\x14'\xa6\xa3\x9e{\xa4\x84\x9fz\xb1\xc45\x8a\xc3\x1b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x12\xb5\xe2\x89E\xbb)i\xf9\xc6Lc\xcc\x05\xb6\xf1\xf8\xd6\xf4\u054a\x01\xa2\x9e\x86\x91;t\x05\x00\x00\u0794\x12\u03cb\x0eFR\x13!\x1a[S\u07f0\xdd'\x1a(,\x12\u0248\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x12\xd2\a\x90\xb7\xd3\xdb\u060c\x81\xa2y\xb8\x12\x03\x9e\x8a`;\u0409V\xf9\x85\u04c6D\xb8\x00\x00\xe0\x94\x12\xd6\re\xb7\xd9\xfcH\x84\v\xe5\xf8\x91\xc7E\xcev\xeeP\x1e\x8a\x04\x85\xe58\x8d\fv\x84\x00\x00\u0794\x12\xd9\x1a\x92\xd7O\xc8a\xa7)dm\xb1\x92\xa1%\xb7\x9fSt\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\x12\u992d*\xd5t\x84\xddp\x05e\xbd\xdbFB;\u067d1\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x12\xf3,\n\x1f-\xaa\xb6v\xfei\xab\xd9\xe0\x185-L\xcdE\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x12\xf4`\xaedl\xd2x\x0f\xd3\\P\xa6\xafK\x9a\xcc\xfa\x85\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x12\xff\xc1\x12\x86\x05\xcb\f\x13p\x9ar\x90Po&\x90\x97q\x93\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x13\x03$F\xe7\xd6\x10\xaa\x00\xec\x8cV\u0275t\xd3l\xa1\xc0\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\x1cy,\x19}\x18\xbd\x04]p$\x93|\x1f\x84\xb6\x0fD8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x1d\xf8\xd30\xeb|\xc7\x14}\nUWo\x05\u078d&\xa8\xb7\x89\n1\x06+\xee\xedp\x00\x00\u07d4\x13\x1f\xae\xd1%a\xbbz\xee\x04\xe5\x18Z\xf8\x02\xb1\xc3C\x8d\x9b\x89\v\xdf<K\xb02\x8c\x00\x00\u07d4\x13!\xb6\x05\x02oO\xfb)j>\x0e\u0733\x90\xc9\xc8V\b\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13!\xcc\xf2\x979\xb9t\xe5\xa5\x16\xf1\x8f:\x846q\xe3\x96B\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13'\xd7Y\xd5n\n\xb8z\xf3~\xcfc\xfe\x01\xf3\x10\xbe\x10\n\x89#\xbc<\xdbh\xa1\x80\x00\x00\u07d4\x13)\xdd\x19\xcdK\xaa\x9f\xc6C\x10\xef\xec\xea\xb2!\x17%\x1f\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x137\x1f\x92\xa5n\xa88\x1eC\x05\x9a\x95\x12\x8b\xdcMC\u0166\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x13<I\x0f\xa5\xbf\u007f7(\x88\xe6\a\xd9X\xfa\xb7\xf9U\xba\xe1\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\x13>O\x15\xe1\xe3\x9cSCY0\xaa\xed\xf3\xe0\xfeV\xfd\xe8C\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x13Ac\xbe\x9f\xbb\xe1\xc5in\xe2U\xe9\v\x13%C\x95\xc3\x18\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x13\\\xec\xd9U\xe5y\x83pv\x920\x15\x93\x03\u0671\x83\x9ff\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x13]\x17\x19\xbf\x03\xe3\xf8f1$y\xfe3\x81\x18\xcd8~p\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x13^\xb8\xc0\xe9\xe1\x01\xde\xed\xec\x11\xf2\xec\xdbf\xae\x1a\xae\x88g\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x13`\xe8}\xf2Li\xeemQ\xc7nsv\u007f\xfe\x19\xa2\x13\x1c\x89\x04\xfc\xc1\xa8\x90'\xf0\x00\x00\u07d4\x13l\x83K\xf1\x112m s\x95)[.X>\xa7\xf35r\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x13mKf+\xbd\x10\x80\xcf\xe4D[\x0f\xa2\x13\x86D5\xb7\xf1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13oI\a\u02b4\x1e'\bK\x98E\x06\x9f\xf2\xfd\f\x9a\xdey\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13t\xfa\xcd{?\x8dhd\x9d`\xd4U\x0e\xe6\x9f\xf0HA3\x89\x0e\x9e\xd6\xe1\x11r\xda\x00\x00\u07d4\x13|\xf3A\xe8Ql\x81X\x14\xeb\xcds\xe6V\x9a\xf1L\xf7\xbc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x13\x84\x8bF\xeau\xbe\xb7\xea\xa8_Y\xd8f\xd7\u007f\xd2L\xf2\x1a\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x13\x9d51\u0252*\xd5bi\xf60\x9a\xa7\x89\xfb$\x85\xf9\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x13\x9eG\x97d\xb4\x99\xd6f \x8cJ\x8a\x04z\x97\x041c\u0749 w!*\xffm\xf0\x00\x00\u07d4\x13\xa5\xee\xcb80]\xf9Iq\xef-\x9e\x17\x9a\xe6\u03ba\xb37\x89\x11\u3ac3\x95\xc6\xe8\x00\x00\u07d4\x13\xac\xad\xa8\x98\n\xff\xc7PI!\xbe\x84\xebID\xc8\xfb\xb2\xbd\x89V\u04aa:\\\t\xa0\x00\x00\u07d4\x13\xb9\xb1\a\x15qL\t\xcf\xd6\x10\u03dc\x98F\x05\x1c\xb1\xd5\x13\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x13\xce3-\xffe\xa6\xab\x938\x97X\x8a\xa2>\x00\t\x80\xfa\x82\x89\x0e\x02\x056\xf0(\xf0\x00\x00\u07d4\x13\xd6z~%\xf2\xb1,\u06c5XP\t\xf8\xac\u011b\x96s\x01\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x13\xde\xe0>7\x99\x95-\a8\x84=K\xe8\xfc\n\x80?\xb2\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe0/\xb4H\xd6\xc8J\xe1}\xb3\x10\xad(m\x05a`\u0695\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x13\xe3!r\x8c\x9cWb\x80X\xe9?\xc8f\xa02\xdd\v\u0690\x89&\xbc\xca#\xfe.\xa2\x00\x00\u07d4\x13\xec\x81\"\x84\x02n@\x9b\xc0f\xdf\xeb\xf9\u0564\xa2\xbf\x80\x1e\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\x14\x01)\xea\xa7f\xb5\xa2\x9f[:\xf2WND\t\xf8\xf6\xd3\xf1\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4\x14\x05\x18\xa3\x19K\xad\x13P\xb8\x94\x9ee\x05e\u07bem\xb3\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\x06\x85M\x14\x9e\b\x1a\xc0\x9c\xb4\xcaV\r\xa4c\xf3\x120Y\x89Hz\x9a0E9D\x00\x00\u07d4\x14\f\xa2\x8f\xf3;\x9ff\xd7\xf1\xfc\x00x\xf8\xc1\xee\xf6\x9a\x1b\xc0\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x14\x0f\xbaX\xdb\xc0H\x03\xd8L!0\xf0\x19x\xf9\xe0\xc71)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x14\x1a^9\xee/h\n`\x0f\xbfo\xa2\x97\u0790\xf3\"\\\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x14%N\xa1&\xb5-\x01B\xda\n~\x18\x8c\xe2U\xd8\xc4qx\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\x14+\x87\xc5\x04?\xfbZ\x91\xdf\x18\xc2\xe1\t\xce\xd6\xfeJq\u06c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14<c\x97R\xca\xee\xcfj\x99}9p\x9f\xc8\xf1\x98x\xc7\xe8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x14=Sk\x8b\x1c\xb8OV\xa3\x9e\v\xc8\x1f\xd5D+\xca\xcc\xe1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x14?_\x16X\xd9\xe5x\xf4\xf3\xd9_\x80\xc0\xb1\xbd93\xcb\u0689P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x14A\x0f\xb3\x10q\x1b\xe0t\xa8\b\x83\xc65\xd0\xefj\xfb%9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14K\x19\xf1\xf6l\xbe1\x83G\u4344\xb1@9FlY\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14RP\xb0nO\xa7\xcb'IB.\xb8\x17\xbd\u068bT\xde_\x89\v\xdf<K\xb02\x8c\x00\x00\u07d4\x14^\x06\x00\xe2\xa9'\xb2\u074d7\x93V\xb4Z.}Q\u04ee\x89\x8a\x02\xab@\v\xb2\u02c0\x00\u07d4\x14^\x1d\xe0\x14y\x11\xcc\u0600\x87_\xbb\xeaa\xf6\xa1B\xd1\x1d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x14c\xa8sU[\xc09~W\\$q\xcfw\xfa\x9d\xb1F\xe0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x14y\xa9\xect\x80\xb7K]\xb8\xfcI\x9b\xe3R\xda\u007f\x84\ue70965\u026d\xc5\u07a0\x00\x00\u07d4\x14z\xf4j\xe9\xcc\u044b\xb3\\\xa0\x1b5;Q\x99\x0eI\xdc\xe1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x14\u007fB\x10\xabX\x04\x94\n\v}\xb8\xc1L(9kb\xa6\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\x83\a\x04\u96aa\xd5\xc5^\x1fP+'\xb2,\x12\xc9\x193\x89!\x9c:{\x19f0\x00\x00\xe0\x94\x14\x9bm\xbd\xe62\xc1\x9fZ\xf4|\xb4\x93\x11K\xeb\u0670<\x1f\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\x14\x9b\xa1\x0f\r\xa2r]\xc7\x04s>\x87\xf5\xa5$\u0288Q^\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\x14\xa75 f6D\x04\xdbP\xf0\xd0\u05cduJ\"\x19\x8e\xf4\x89e\xea=\xb7UF`\x00\x00\u07d4\x14\xab\x16K;RL\x82\u05ab\xfb\xc0\u0783\x11&\xae\x8d\x13u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x14\xb1`>\xc6+ \x02 3\xee\xc4\xd6\xd6eZ\xc2J\x01Z\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x14\xc6;\xa2\u0731\xddM\xf3=\u06b1\x1cO\x00\a\xfa\x96\xa6-\x8a\x03HA\xb6\x05z\xfa\xb0\x00\x00\xe0\x94\x14\xcd\u077c\x8b\t\xe6gZ\x9e\x9e\x05\t\x1c\xb9\"8\u00de\x1e\x8a\x01\x14x\xb7\xc3\n\xbc0\x00\x00\u07d4\x14\xd0\n\xad9\xa0\xa7\u045c\xa0SP\xf7\xb07'\xf0\x8d\xd8.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x14\xee\xc0\x9b\xf0>5+\xd6\xff\x1b\x1e\x87k\xe6d\xce\xff\xd0\u03c9\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\x14\xf2!\x15\x95\x18x;\u0127\x06go\xc4\xf3\xc5\xee@X)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x14\xfc\xd19\x1e}s/Avl\xda\u0344\xfa\x1d\xeb\x9f\xfd\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x0e=\xbc\xbc\xfc\x84\xcc\xf8\x9bsBwc\xa5e\xc2>`\u0409\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\x15\x18b{\x885\x1f\xed\xe7\x96\xd3\xf3\b3d\xfb\u0508{\f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u0794\x15\"J\xd1\xc0\xfa\xceF\xf9\xf5V\xe4wJ0%\xad\x06\xbdR\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x15/+\xd2)\xdd\xf3\xcb\x0f\xda\xf4U\xc1\x83 \x9c\x0e\x1e9\xa2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15/N\x86\x0e\xf3\xee\x80jP'w\xa1\xb8\xdb\xc9\x1a\x90vh\x89 \x86\xac5\x10R`\x00\x00\u07d4\x15<\b\xaa\x8b\x96\xa6\x11\xefc\xc0%>*C4\x82\x9eW\x9d\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x15<\xf2\x84,\xb9\u0787l'o\xa6Gg\u0468\xec\xf5s\xbb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15>\xf5\x8a\x1e.z>\xb6\xb4Y\xa8\n\xb2\xa5G\xc9A\x82\xa2\x8a\x14T+\xa1*3|\x00\x00\x00\u07d4\x15DY\xfa/!1\x8e44D\x97\x89\xd8&\xcd\xc1W\f\xe5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15G\xb9\xbfz\xd6bt\xf3A8'#\x1b\xa4\x05\ue308\xc1\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\x15H\xb7p\xa5\x11\x8e\u0787\u06e2\xf6\x903\u007fam\u60eb\x89\x1c\x99V\x85\u0fc7\x00\x00\u07d4\x15R\x83P\xe0\xd9g\n.\xa2\u007f{J3\xb9\xc0\xf9b\x1d!\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x15[7y\xbbmV4./\u0681{[-\x81\xc7\xf4\x13'\x89\x02\xb8\xaa:\al\x9c\x00\x00\u07d4\x15e\xaf\x83~\xf3\xb0\xbdN+#V\x8dP#\xcd4\xb1d\x98\x89\x15Q\xe9rJ\u013a\x00\x00\u07d4\x15f\x91\x80\xde\u2558\x86\x9b\b\xa7!\xc7\xd2LL\x0e\xe6?\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x15r\xcd\xfa\xb7*\x01\u0396\x8ex\xf5\xb5D\x8d\xa2\x98S\xfb\u074a\x01\x12blI\x06\x0f\xa6\x00\x00\xe0\x94\x15uY\xad\xc5Wd\xccm\xf7\x93#\t%4\xe3\xd6dZf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x15x\xbd\xbc7\x1bM$8E3\x05V\xff\xf2\xd5\xefM\xffg\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x15~\xb3\xd3\x11;\u04f5\x97qM:\x95N\xdd\x01\x89\x82\xa5\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x84\xa2\xc0f\xb7\xa4U\xdb\u05ae(\a\xa73N\x83\xc3_\xa5\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\u07d4\x15\x87F\x86\xb6s=\x10\xd7\x03\xc9\xf9\xbe\xc6\xc5.\xb8b\x8dg\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x15\x8a\ra\x92S\xbfD2\xb5\xcd\x02\u01f8b\xf7\u00b7V6\x89\a[\xac|[\x12\x18\x80\x00\u07d4\x15\x98\x12y\x82\xf2\xf8\xad;k\x8f\xc3\xcf'\xbfax\x01\xba+\x89\t`\xdbwh\x1e\x94\x00\x00\xe0\x94\x15\x9a\xdc\xe2z\xa1\vG#d)\xa3JZ\xc4,\xad[d\x16\x8a\x06\xbf\x90\xa9n\xdb\xfaq\x80\x00\u07d4\x15\xa0\xae\xc3\u007f\xf9\xff=T\t\xf2\xa4\xf0\xc1!*\xac\xcb\x02\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x15\xaaS\r\xc3iX\xb4\xed\xb3\x8e\xeem\xd9\xe3\xc7}L\x91E\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\xac\xb6\x15h\xecJ\xf7\xea(\x198a\x81\xb1\x16\xa6\xc5\xeep\x8a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\x15\xb9o0\xc2;\x86d\xe7I\x06Q\x06k\x00\xc49\x1f\xbf\x84\x89\x16B\xe9\xdfHv)\x00\x00\u07d4\x15\xc7\xed\xb8\x11\x8e\xe2{4\"\x85\xebY&\xb4z\x85[\u01e5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x15\u0654hPz\xa0A?\xb6\r\xca*\xdc\u007fV\x9c\xb3kT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x15\u06f4\x8c\x980\x97d\xf9\x9c\xed6\x92\xdc\xca5\xee0k\xac\x8a\x1f\u00c4+\xd1\xf0q\xc0\x00\x00\xe0\x94\x15\u072f\xcc+\xac\xe7\xb5[T\xc0\x1a\x1cQF&\xbfa\xeb\u060a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x15\u3d44\x05kb\xc9s\xcf^\xb0\x96\xf1s>T\xc1\\\x91\x892\xc7Z\x02#\xdd\xf3\x00\x00\u07d4\x15\xeb\xd1\xc7\xca\u04af\xf1\x92u\xc6W\xc4\xd8\b\xd0\x10\xef\xa0\xf5\x89\n\xdf0\xbap\u0217\x00\x00\u07d4\x15\xee\x0f\xc6>\xbf\x1b\x1f\u011d{\xb3\x8f\x88c\x82:.\x17\u0489g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x15\xf1\xb3R\x11\rh\x90\x1d\x8fg\xaa\xc4jl\xfa\xfe\x03\x14w\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x15\xf2\xb7\xb1d2\xeeP\xa5\xf5[A#/c4\xedX\xbd\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x16\x01\x9aM\xaf\xabC\xf4\u067fAc\xfa\xe0\x84}\x84\x8a\xfc\xa2\x89\x01[\xc7\x019\xf7J\x00\x00\u07d4\x16\x02&\xef\xe7\xb5:\x8a\xf4b\xd1\x17\xa0\x10\x80\x89\xbd\xec\xc2\u0449\n\xdf0\xbap\u0217\x00\x00\u07d4\x16\f\xebo\x98\x0e\x041_S\xc4\xfc\x98\x8b+\xf6\x9e(M}\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94\x16\x1c\xafZ\x97*\u0383y\xa6\u0420J\xe6\xe1c\xfe!\xdf+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x16\x1d&\xefgY\xba[\x9f \xfd\xcdf\xf1a2\xc3RA^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16!\x10\xf2\x9e\xac_}\x02\xb5C\xd8\xdc\u057bY\xa5\xe3;s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16+\xa5\x03'b\x14\xb5\t\xf9u\x86\xbd\x84!\x10\xd1\x03\xd5\x17\x8a\x01\xe7\xff\u0609\\\"h\x00\x00\u07d4\x16-v\xc2\xe6QJ:\xfbo\xe3\xd3\u02d3\xa3\\Z\xe7\x83\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16;\xadJ\x12+E}d\xe8\x15\nA>\xaeM\a\x02>k\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u07d4\x16<\u023e\"vF\xcb\tq\x91Y\xf2\x8e\u041c]\xc0\xdc\xe0\x89Hz\x9a0E9D\x00\x00\u07d4\x16=\xcas\xd7\xd6\xea?>`b2*\x874\x18\f\vx\uf25ft \x03\xcb}\xfc\x00\x00\u07d4\x16Mz\xac>\xec\xba\uc86dQ\x91\xb7S\xf1s\xfe\x12\xec3\x89(VR\xb8\xa4hi\x00\x00\u07d4\x16Rl\x9e\u07d4>\xfaOm\x0f\v\xae\x81\xe1\x8b1\xc5@y\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x16S\x05\xb7\x872.%\xdcj\xd0\xce\xfelo3Fx\xd5i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16e\xab\x179\xd7\x11\x19\xeea2\xab\xbd\x92j'\x9f\xe6yH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x16k\xf6\u06b2-\x84\x1bHl8\xe7\xbaj\xb3:\x14\x87\ud30a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x16v\x99\xf4\x8ax\xc6\x15Q%\x15s\x99X\x993\x12WO\a\x89\x02\x1d;\xd5^\x80<\x00\x00\u07d4\x16x\xc5\xf2\xa5\"92%\x19ca\x89OS\xccu/\xe2\xf3\x89h\xf3e\xae\xa1\xe4@\x00\x00\u07d4\x16|\xe7\xdee\xe8G\bYZRT\x97\xa3\xeb^ZfPs\x89\x1f1Gsfo\xc4\x00\x00\u07d4\x16~>:\xe2\x003HE\x93\x92\xf7\xdf\xceD\xaf|!\xadY\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x16\x80\xce\xc5\x02\x1e\xe90P\xf8\xae\x12rQ\x83\x9et\xc1\xf1\xfd\x8a\x02\xc6\x14a\xe5\xd7C\u0580\x00\u07d4\x16\x81j\xac\x0e\xde\r-<\xd4B\xday\xe0c\x88\x0f\x0f\x1dg\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x16\x8bP\x19\xb8\x18i\x16D\x83_\xe6\x9b\xf2)\xe1q\x12\xd5,\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\u07d4\x16\x8b\xde\xc8\x18\xea\xfcm)\x92\xe5\xefT\xaa\x0e\x16\x01\xe3\xc5a\x8967Pz0\xab\xeb\x00\x00\u07d4\x16\x8d0\xe5?\xa6\x81\t+R\xe9\xba\xe1Z\r\xcbA\xa8\u027b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x16\x9b\xbe\xfcA\xcf\xd7\xd7\u02f8\xdf\xc60 \xe9\xfb\x06\u0515F\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xa5\x8e\x98]\xcc\xd7\a\xa5\x94\u0453\xe7\u0327\x8b]\x02xI\x89I\xb9\u029aiC@\x00\x00\u07d4\x16\xa9\xe9\xb7:\u92c6M\x17(y\x8b\x87f\xdb\xc6\xea\x8d\x12\x893\xe7\xb4K\r\xb5\x04\x00\x00\u07d4\x16\xaaR\xcb\vUG#\xe7\x06\x0f!\xf3'\xb0\xa6\x83\x15\xfe\xa3\x89\r\x8drkqw\xa8\x00\x00\u07d4\x16\xab\xb8\xb0!\xa7\x10\xbd\u01ce\xa54\x94\xb2\x06\x14\xffN\xaf\xe8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x16\xaf\xa7\x87\xfc\x9f\x94\xbd\xffiv\xb1\xa4/C\n\x8b\xf6\xfb\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xba\xe5\xd2N\xff\x91w\x8c\u064bM:\x1c\xc3\x16/D\xaaw\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\x16\xbc@!Z\xbb\u066e](\v\x95\xb8\x01\vE\x14\xff\x12\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x16\xbeu\u9299Z9R\"\xd0\v\u05df\xf4\xb6\xe68\u144a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\x16\xc1\xbf[}\xc9\xc8<\x17\x9e\xfa\xcb\xcf.\xb1t\xe3V\x1c\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x16\u01f3\x1e\x8c7b\x82\xac\"qr\x8c1\xc9^5\xd9R\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xf3\x13\u03ca\xd0\x00\x91J\n\x17m\u01a44+y\xec%8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x16\xff\xac\x84\x03)@\xf0\x12\x1a\tf\x8b\x85\x8a~y\xff\xa3\xbb\x89\xd2J\xdan\x10\x87\x11\x00\x00\xe0\x94\x17\x03\xb4\xb2\x92\xb8\xa9\xde\xdd\xed\xe8\x1b\xb2]\x89\x17\x9fdF\xb6\x8a\x04+e\xa4U\xe8\xb1h\x00\x00\u07d4\x17\x04\x93\x11\x10\x1d\x81~\xfb\x1de\x91\x0ff6b\xa6\x99\u024c\x89lh\xcc\u041b\x02,\x00\x00\u07d4\x17\x04\xce\xfc\xfb\x131\xeczx8\x8b)9>\x85\xc1\xafy\x16\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\n\x88\xa8\x99\u007f\x92\xd287\x0f\x1a\xff\xde\xe64pP\xb0\x13\x89\xa2\xacw5\x14\x880\x00\x00\u07d4\x17\x10\x8d\xab,P\xf9\x9d\xe1\x10\u1cf3\xb4\u0342\xf5\xdf(\xe7\x895 ;g\xbc\xca\xd0\x00\x00\xe0\x94\x17\x12[Y\xacQ\xce\xe0)\xe4\xbdx\xd7\xf5\x94}\x1e\xa4\x9b\xb2\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4\x17\x1a\u0660K\xed\u0238a\xe8\xedK\xdd\xf5qx\x13\xb1\xbbH\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17\x1c\xa0*\x8bmb\xbfL\xa4~\x90i\x14\a\x98a\x97,\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\"\xc4\xcb\xe7\n\x94\xb6U\x9dBP\x84\xca\xee\xd4\xd6\xe6n!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17X\vvotSR\\\xa4\u01a8\x8b\x01\xb5\x05p\xea\b\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x17X\x9al\x00jT\xca\xd7\x01\x03\x12:\xae\n\x82\x13_\u07b4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x17Z\x18::#_\xfb\xb0;\xa85gRg\"\x94\x17\xa0\x91\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x17_\xee\xea*\xa4\xe0\xef\xda\x12\xe1X\x8d/H2\x90\xed\xe8\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x17e6\x1c.\xc2\xf86\x16\u0383c\xaa\xe2\x10%\xf2Vo@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17gR\\_Z\"\xed\x80\xe9\xd4\xd7q\x0f\x03b\u049e\xfa3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x17v%`\xe8*\x93\xb3\xf5\"\xe0\xe5$\xad\xb8a,:tp\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x17}\xaex\xbc\x01\x13\xd8\u04dcD\x02\xf2\xa6A\xae*\x10Z\xb8\x89b\x92BV \xb4H\x00\x00\xe0\x94\x17\x84\x94\x8b\xf9\x98H\u021eDV8PM\u0598'\x1bY$\x8a\x01GLA\r\x87\xba\xee\x00\x00\u07d4\x17\x88\u069bW\xfd\x05\xed\xc4\xff\x99\xe7\xfe\xf3\x01Q\x9c\x8a\n\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\x8e\xafk\x85T\xc4]\xfd\xe1kx\xce\f\x15\u007f.\xe3\x13Q\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x17\x96\x1dc;\xcf \xa7\xb0)\xa7\xd9K}\xf4\xda.\xc5B\u007f\x89\fo\xf0p\U000532c0\x00\u07d4\x17\x96\xbc\xc9{\x8a\xbcq\u007fKJ|k\x106\xea!\x82c\x9f\x89\x13A\xf9\x1c\xd8\xe3Q\x00\x00\u07d4\x17\x99=1*\xa1\x10iW\x86\x8fjU\xa5\xe8\xf1/w\xc8C\x89\x18e\xe8\x14\xf4\x14.\x80\x00\u07d4\x17\x9a\x82^\x0f\x1fn\x98S\tf\x84e\xcf\xfe\xd46\xf6\xae\xa9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xb2\xd6\xcfe\xc6\xf4\xa3G\xdd\xc6W&U5M\x8aA+)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xb8\a\xaf\xa3\xdd\xd6G\xe7#T.{R\xfe\xe3\x95'\xf3\x06\x89\x15\xaf@\xff\xa7\xfc\x01\x00\x00\u07d4\x17\xc0G\x86W\xe1\xd3\xd1z\xaa3\x1d\xd4)\xce\u03d1\xf8\xae]\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\x17\xc0\xfe\xf6\x98l\xfb.@A\xf9\x97\x9d\x99@\xb6\x9d\xff=\xe2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x17\u0511\x8d\xfa\xc1]w\xc4\u007f\x9e\xd4\x00\xa8P\x19\rd\xf1Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xd5!\xa8\xd9w\x90#\xf7\x16M#<;d \xff\xd2#\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x17\xd91\xd4\xc5b\x94\u073ew\xc8e[\xe4i_\x00mJ<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xdfIQ\x8ds\xb1)\xf0\xda6\xb1\u0274\f\xb6d \xfd\u01ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x17\xe4\xa0\xe5+\xac>\xe4N\xfe\tT\xe7S\u0538]dN\x05\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xe5\x84\xe8\x10\xe5gp,a\xd5]CK4\u0375\xee0\xf6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x17\xe8.px\xdcO\xd9\xe8y\xfb\x8aPf\u007fS\xa5\xc5E\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x17\xe8o;[0\xc0\xbaY\xf2\xb2\xe8XB[\xa8\x9f\n\x10\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x17\xee\x9fT\xd4\xdd\xc8Mg\x0e\xff\x11\xe5Je\x9f\xd7/DU\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94\x17\xefJ\xcc\x1b\xf1G\xe3&t\x9d\x10\xe6w\xdc\xff\xd7o\x9e\x06\x8a\bwQ\xf4\xe0\xe1\xb50\x00\x00\u07d4\x17\xf1F2\xa7\xe2\x82\v\xe6\xe8\xf6\u07c25X(=\xad\xab-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x17\xf5#\xf1\x17\xbc\x9f\xe9x\xaaH\x1e\xb4\xf5V\x17\x117\x1b\u0209li\xf7>)\x13N\x00\x00\u07d4\x17\xfd\x9bU\x1a\x98\xcba\xc2\xe0\u007f\xbfA\xd3\xe8\u02650\u02e5\x89\x01v\x8c0\x81\x93\x04\x80\x00\u07d4\x18\x04x\xa6U\u05cd\x0f;\fO +aH[\xc4\x00/\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\x13l\x9d\xf1g\xaa\x17\xb6\xf1\x8e\"\xa7\x02\u020fK\u0082E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x18\x15'\x9d\xff\x99R\xda;\xe8\xf7rI\xdb\xe2\"C7{\xe7\x8a\x01\x01|\xb7n{&d\x00\x00\u07d4\x18\x1f\xbb\xa8R\xa7\xf5\x01x\xb1\xc7\xf0>\xd9\xe5\x8dT\x16))\x89$\x1a\x9bOaz(\x00\x00\xe0\x94\x18'\x03\x9f\tW\x02\x94\b\x8f\xdd\xf0G\x16\\3\u65a4\x92\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x18-\xb8R\x93\xf6\x06\u8248\xc3pL\xb3\xf0\xc0\xbb\xbf\xcaZ\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18H\x00<%\xbf\u052a\x90\xe7\xfc\xb5\u05f1k\xcd\f\xff\xc0\u060965\u026d\xc5\u07a0\x00\x00\xe0\x94\x18JO\v\xebq\xff\xd5X\xa6\xb6\xe8\xf2(\xb7\x87\x96\xc4\xcf>\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\x18M\x86\xf3Fj\xe6h;\x19r\x99\x82\xe7\xa7\u1903G\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x18Q\xa0c\xcc\xdb0T\x90w\xf1\xd19\xe7-\xe7\x97\x11\x97\u0549lk\x93[\x8b\xbd@\x00\x00\u07d4\x18UF\xe8v\x8dPhs\x81\x8a\xc9u\x1c\x1f\x12\x11j;\xef\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x18X\xcf\x11\xae\xa7\x9fS\x98\xad+\xb2\"g\xb5\xa3\xc9R\xeat\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\xe0\x94\x18Z\u007f\u012c\xe3h\xd23\xe6 \xb2\xa4Y5f\x12\x92\xbd\xf2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x18d\xa3\u01f4\x81UD\x8cT\u020cp\x8f\x16g\tsm1\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x18j\xfd\xc0\x85\xf2\xa3\xdc\xe4a^\xdf\xfb\xad\xf7\x1a\x11x\x0fP\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x18k\x95\xf8\xe5\xef\xfd\xdc\xc9O\x1a1[\xf0)];\x1e\xa5\x88\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x18}\x9f\f\a\xf8\xebt\xfa\xaa\xd1^\xbc{\x80Dt\x17\xf7\x82\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x18\x95\xa0\xebJCrr/\xcb\u016f\xe6\x93o(\x9c\x88\xa4\x19\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x18\x99\xf6\x9fe;\x05\xa5\xa6\xe8\x1fH\a\x11\u041b\xbf\x97X\x8c\x89i\xfb\x13=\xf7P\xac\x00\x00\u07d4\x18\xa6\xd2\xfcR\xbes\b@#\xc9\x18\x02\xf0[\xc2JK\xe0\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\xb0@|\xda\xd4\xceR`\x06#\xbd^\x1fj\x81\xaba\xf0&\x89\x11Q\xcc\xf0\xc6T\u0180\x00\u07d4\x18\xb8\xbc\xf9\x83!\xdaa\xfbN>\xac\xc1\xecT\x17'-\xc2~\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\x18\xc6r:gS)\x9c\xb9\x14G}\x04\xa3\xbd!\x8d\xf8\xc7u\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x18\xe1\x13\xd8\x17|i\x1aa\xbexXR\xfa[\xb4z\uef6f\x89Hz\x9a0E9D\x00\x00\xe0\x94\x18\xe4\xceGH;S\x04\n\u06eb5\x17,\x01\xefdPn\f\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x18\xe52C\x98\x1a\xab\xc8v}\xa1\fsD\x9f\x13\x91V\x0e\xaa\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x18\xfa\x86%\xc9\u0704<x\u01eb%\x9f\xf8|\x95\x99\xe0\u007f\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x18\xfb\t\x18\x8f'\xf1\x03\x8ee@1\x92Ob\x8a!\x06p=\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x18\xfc\xcfb\xd2\xc39TS\xb7X{\x9e&\xf5\xcf\xf9\xebt\x82\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x19\x13\x13RR8\xa2\x1cvtW\xa9\x13t\xf0\"\x00\xc5TH\x89\x06O_\xdfIOx\x00\x00\u07d4\x19\x14\xf1\xeb\x95\xd1'~\x93\xb6\xe6\x1bf\x8b}w\xf1:\x11\xa1\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x19#\xcf\u018b\x13\xea~ U\x806E\xc1\xe3 \x15k\u060d\x89Hz\x9a0E9D\x00\x00\u07d4\x193j#m\xeduXrA\x1f.\x04\x91\xd8>>\x00\x15\x9e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\x193\xe34\xc4\x0f:\u02ed\f\v\x85\x11X i$\xbe\xca:\x8a\x01\x99^\xaf\x01\xb8\x96\x18\x80\x00\xe0\x94\x197\xc5\xc5\x15\x05uS\u033dF\u0546dU\xcef)\x02\x84\x8a\xd3\xc2\x1b\xce\xcc\xed\xa1\x00\x00\x00\u07d4\x19:\xc6Q\x83e\x18\x00\xe25\x80\xf8\xf0\xea\u04fbY~\xb8\xa4\x89\x02\xb6*\xbc\xfb\x91\n\x00\x00\u07d4\x19=7\xed4}\x1c/N55\r\x9aDK\xc5|\xa4\xdbC\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\x19@\u0713d\xa8R\x16_GAN'\xf5\x00$E\xa4\xf1C\x8a\x02L-\xffj<|H\x00\x00\u07d4\x19E\xfe7\u007f\xe6\u0537\x1e>y\x1fo\x17\xdb$<\x9b\x8b\x0f\x89vy\u7fb9\x886\x00\x00\u07d4\x19Jk\xb3\x02\xb8\xab\xa7\xa5\xb5y\u07d3\xe0\xdf\x15t\x96v%\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x19L\ubd12\x98\x82\xbf;K\xf9\x86L+\x1b\x0fb\u0083\xf9\x89\x1e\xf8aS\x1ft\xaa\x00\x00\u07d4\x19O\xf4J\xef\xc1{\xd2\x0e\xfdz LG\xd1b\f\x86\xdb]\x89\xa2\x99\th\u007fj\xa4\x00\x00\xe0\x94\x19O\xfex\xbb\xf5\xd2\r\u044a\x1f\x01\xdaU.\x00\xb7\xb1\x1d\xb1\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x19S1>*\xd7F#\x9c\xb2'\x0fH\xaf4\u063b\x9cDe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19W\x1a+\x8f\x81\u01bc\xf6j\xb3\xa1\x00\x83)V\x17\x15\x00\x03\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\x19h}\xaa9\xc3h\x13\x9bn{\xe6\r\xc1u:\x9f\f\xbe\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x19l\x02!\nE\n\xb0\xb3cpe_qz\xa8{\xd1\xc0\x04\x89\x0e\x10\xac\xe1W\xdb\xc0\x00\x00\u07d4\x19n\x85\xdf~s+J\x8f\x0e\xd06#\xf4\u06dd\xb0\xb8\xfa1\x89\x01%\xb9/\\\xef$\x80\x00\u07d4\x19s+\xf9s\x05]\xbd\x91\xa4S:\u06a2\x14\x9a\x91\u04c3\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19vr\xfd9\xd6\xf2F\xcef\xa7\x90\xd1:\xa9\"\xd7\x0e\xa1\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x19y\x8c\xbd\xa7\x15\ua69b\x9dj\xab\x94,U\x12\x1e\x98\xbf\x91\x89A\rXj \xa4\xc0\x00\x00\u07d4\x19\x8b\xfc\xf1\xb0z\xe3\b\xfa,\x02\x06\x9a\xc9\xda\xfeq5\xfbG\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x19\x8e\xf1\xec2Z\x96\xcc5Lrf\xa08\xbe\x8b\\U\x8fg\x8a\x80\xd1\xe47>\u007f!\xda\x00\x00\xe0\x94\x19\x91\x8a\xa0\x9e}IN\x98\xff\xa5\xdbP5\b\x92\xf7\x15j\u018a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x19\xb3k\f\x87\xeafN\xd8\x03\x18\xdcw\xb6\x88\xdd\xe8}\x95\xa5\x89i\x9fI\x98\x020=\x00\x00\u07d4\x19\u07d4E\xa8\x1c\x1b=\x80J\xea\xebon NB6f?\x89\x02\x06\xd9NjI\x87\x80\x00\u07d4\x19\xe5\u07a37\n,tj\xae4\xa3|S\x1fA\xda&N\x83\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x19\xe7\xf3\xeb{\xf6\u007f5\x99 \x9e\xbe\b\xb6*\xd32\u007f\x8c\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xe9Nb\x00P\xaa\xd7f\xb9\xe1\xba\xd91#\x83\x12\u053fI\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4\x19\xec\xf2\xab\xf4\f\x9e\x85{%/\xe1\xdb\xfd=L]\x8f\x81n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x19\xf5\xca\xf4\xc4\x0ei\b\x81<\aE\xb0\xae\xa9Xm\x9d\xd91\x89#\xfe\xd9\xe1\xfa+`\x00\x00\u07d4\x19\xf6C\xe1\xa8\xfa\x04\xae\x16\x00`(\x13\x833\xa5\x9a\x96\u0787\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x19\xf9\x9f,\vF\u0389\x06\x87]\xc9\xf9\n\xe1\x04\xda\xe3U\x94\x89\xf4WZ]M\x16*\x00\x00\u07d4\x19\xff$O\xcf\xe3\xd4\xfa/O\u065f\x87\xe5[\xb3\x15\xb8\x1e\xb6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1a\x04\xce\xc4 \xadC\"\x15$mw\xfe\x17\x8d3\x9e\u0435\x95\x89\x11!a\x85\u009fp\x00\x00\xe0\x94\x1a\x04\xd58\x9e\xb0\x06\xf9\u0388\f0\xd1SS\xf8\xd1\x1cK1\x8a\x03\x9d\x84\xb2\x18m\xc9\x10\x00\x00\u07d4\x1a\bA\xb9*\u007fpuV\x9d\xc4b~kv\u02b0Z\u0791\x89Rf<\u02b1\xe1\xc0\x00\x00\xe0\x94\x1a\b]C\xec\x92AN\xa2{\x91O\xe7g\xb6\xd4k\x1e\xefD\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\x1a\t\xfd\xc2\u01e2\x0e#WK\x97\u019e\x93\u07bag\xd3r \x89lO\xd1\xee$nx\x00\x00\u07d4\x1a\n\x1d\u07f01\xe5\xc8\xcc\x1dF\xcf\x05\x84-P\xfd\xdcq0\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1a\x1c\x9a&\xe0\xe0$\x18\xa5\xcfh}\xa7Z'\\b,\x94@\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1a \x1bC'\u03a7\xf3\x99\x04bF\xa3\xc8~n\x03\xa3\u0368\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a$4\xccwD\"\u050dS\u055c]V,\u0384\a\xc9K\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\x1a%\xe1\u017c~_P\xec\x16\xf8\x88_!\x0e\xa1\xb98\x80\x0e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1a&\x94\xec\a\xcf^Mh\xba@\xf3\xe7\xa1LS\xf3\x03\x8cn\x8966\xcd\x06\xe2\xdb:\x80\x00\u07d4\x1a5 E5\x82\xc7\x18\xa2\x1cB7[\xc5\as%RS\xe1\x89*\xd3s\xcef\x8e\x98\x00\x00\xe0\x94\x1a7n\x1b-/Y\ai\xbb\x85\x8dEu2\rN\x14\x99p\x8a\x01\x06q%v9\x1d\x18\x00\x00\u07d4\x1a:3\x0eO\xcbi\xdb\xef^i\x01x;\xf5\x0f\xd1\xc1SB\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1aN\u01a0\xae\u007fZ\x94'\xd2=\xb9rL\r\f\xff\xb2\xab/\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\x1aP^b\xa7N\x87\xe5wG>O:\xfa\x16\xbe\xdd<\xfaR\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x1a^\xe53\xac\xbf\xb3\xa2\xd7m[hRw\xb7\x96\xc5j\x05+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1adJP\xcb\u00ae\xe8#\xbd+\xf2C\xe8%\xbeMG\xdf\x02\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x1apD\xe28?\x87\b0[I[\xd1\x17k\x92\xe7\xef\x04:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1ay\xc7\xf4\x03\x9cg\xa3\x9du\x13\x88L\xdc\x0e,4\"$\x90\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\x89\x89\x9c\xbe\xbd\xbbd\xbb&\xa1\x95\xa6<\bI\x1f\u035e\xee\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\x8a\\\xe4\x14\u079c\xd1r\x93~7\xf2\u055c\xffq\xceW\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1a\x95\xa8\xa8\b.FR\xe4\x17\r\xf9'\x1c\xb4\xbbC\x05\xf0\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x1a\x95\u0277Tk]\x17\x86\u00c5\x8f\xb1#dF\xbc\f\xa4\u0389j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1a\x98~?\x83\xdeu\xa4/\x1b\xde|\x99|\x19!{J_$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1a\x9ep/8]\xcd\x10^\x8b\x9f\xa4(\xee\xa2\x1cW\xffR\x8a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1a\xa1\x02\x1fU\n\xf1X\xc7Gf\x8d\xd1;F1`\xf9Z@\x89O\xb0Y\x1b\x9b08\x00\x00\u07d4\x1a\xa2v\x99\xca\u068d\u00e7oy3\xaaf\xc7\x19\x19\x04\x0e\x88\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x1a\xa4\x02p\xd2\x1e\\\u0786\xb61m\x1a\xc3\xc53IKy\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1a\xb5:\x11\xbc\xc6=\u07ea@\xa0+\x9e\x18d\x96\u037b\x8a\xff\x89l?*\xac\x80\f\x00\x00\x00\u07d4\x1a\xbcN%;\b\n\xebCy\x84\xab\x05\xbc\xa0\x97\x9a\xa4>\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xc0\x89\u00fcM\x82\xf0j \x05\x1a\x9ds-\xc0\xe74\xcba\x89%\xf6\x9dc\xa6\xce\x0e\x00\x00\xe0\x94\x1a\xd4V>\xa5xk\xe1\x15\x995\xab\xb0\xf1\u0547\x9c>sr\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xd7- \xa7n\u007f\xcckv@X\xf4\x8dA}Io\xa6\u0349lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1a\xda\xf4\xab\xfa\x86}\xb1\u007f\x99\xafj\xbe\xbfpz<\xf5]\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1a\xf6\x03C6\x0e\v-u%R\x107W \xdf!\xdb\\}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1a\xfc\u0145\x89l\xd0\xed\xe1)\xee-\xe5\xc1\x9e\xa8\x11T\vd\x89\xaf*\xba\f\x8e[\xef\x80\x00\u07d4\x1b\x05\xeajj\u022f|\xb6\xa8\xb9\x11\xa8\xcc\xe8\xfe\x1a*\xcf\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\v1\xaf\xffKm\xf3e:\x94\xd7\xc8yx\xae5\xf3J\xae\x89\x139\x10E?\xa9\x84\x00\x00\u07d4\x1b\r\ah\x17\xe8\u058e\xe2\xdfN\x1d\xa1\xc1\x14-\x19\x8cD5\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x1b\x13\ro\xa5\x1d\\H\xec\x8d\x1dR\u070a\"{\xe8s\\\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b#\u02c6cUHq\xfb\xbe\r\x9e`9~\xfbo\xae\xdc>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1b&9X\x8bU\xc3D\xb0#\xe8\xde_\xd4\b{\x1f\x04\x03a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x1b9 \xd0\x01\xc4>r\xb2N|\xa4o\x0f\xd6\xe0\xc2\n_\xf2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1b<\xb8\x1eQ\x01\x1bT\x9dx\xbfr\v\r\x92J\xc7c\xa7\u008av\x95\xa9, \xd6\xfe\x00\x00\x00\u07d4\x1bC#,\xcdH\x80\xd6\xf4o\xa7Q\xa9l\xd8$s1XA\x89\x04V9\x18$O@\x00\x00\u07d4\x1bK\xbc\xb1\x81e!\x1b&[(\a\x16\xcb?\x1f!!v\xe8\x89\x19\x9a\xd3}\x03\xd0`\x80\x00\u07d4\x1bM\a\xac\u04c1\x83\xa6\x1b\xb2x=+{\x17\x8d\xd5\x02\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1bckzIo\x04MsYYn5:\x10F\x16Cok\x89\x13\x88\xea\x95\xc3?\x1d\x00\x00\u07d4\x1bd\x95\x89\x12@\xe6NYD\x93\xc2f!q\xdb^0\xce\x13\x89\tX\x87\u0595\xedX\x00\x00\u07d4\x1bf\x10\xfbh\xba\xd6\xed\x1c\xfa\xa0\xbb\xe3:$\xeb.\x96\xfa\xfb\x89\b=lz\xabc`\x00\x00\u07d4\x1by\x903\xefm\xc7\x12x\"\xf7EB\xbb\"\xdb\xfc\t\xa3\b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1b~\xd9t\xb6\xe24\u0381$t\x98B\x9a[\u0520\xa2\xd19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x82o\xb3\xc0\x12\xb0\xd1Y\u253a[\x8aI\x9f\xf3\xc0\xe0<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\x8a\xa0\x16\f\u05df\x00_\x88Q\nqI\x13\xd7\n\u04fe3\x89\n\xef\xfb\x83\a\x9a\xd0\x00\x00\xe0\x94\x1b\x8b\xd6\xd2\xec\xa2\x01\x85\xa7\x8e}\x98\xe8\xe1\x85g\x8d\xacH0\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x1b\x9b-\u0096\x0eL\xb9@\x8ft\x05\x82|\x9bY\a\x16\x12\xfd\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1b\xa9\"\x8d8\x87'\xf3\x89\x15\x0e\xa0;s\xc8-\xe8\xeb.\t\x8a\x01\x89t\xfb\xe1w\xc9(\x00\x00\u07d4\x1b\xa9\xf7\x99~S\x87\xb6\xb2\xaa\x015\xac$R\xfe6\xb4\xc2\r\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\x1b\xba\x03\xffkJ\u057f\x18\x18J\xcb!\xb1\x88\xa3\x99\xe9\xebJ\x89a\t=|,m8\x00\x00\u07d4\x1b\xbc\x19\x9eXg\x90\xbe\x87\xaf\xed\xc8I\xc0G&t\\]{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1b\xbc`\xbc\xc8\x0e\\\xdc5\xc5Aj\x1f\n@\xa8=\xae\x86{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xc4L\x87a#\x1b\xa1\xf1\x1f_\xaa@\xfaf\x9a\x01>\x12\u0389\v\tR\xc4Z\xea\xad\x00\x00\u07d4\x1b\xcf4A\xa8f\xbd\xbe\x960\t\xce3\xc8\x1c\xbb\x02a\xb0,\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\x1b\u048c\xd5\u01ca\xeeQ5|\x95\xc1\xef\x925\xe7\xc1\x8b\xc8T\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xd8\xeb\xaavt\xbb\x18\u1458\xdb$OW\x03\x13\a_C\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\x1b\xd9\t\xac\rJ\x11\x02\xec\x98\xdc\xf2\u0329j\n\xdc\u05e9Q\x89\x01\x16Q\xac>zu\x80\x00\u07d4\x1b\xe3T,6\x13hte\xf1Zp\xae\xeb\x81f+e\u0328\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1b\xeaM\xf5\x12/\xaf\u07b3`~\xdd\xda\x1e\xa4\xff\u06da\xbf*\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4\x1b\xecM\x02\u0385\xfcH\xfe\xb6$\x89\x84\x1d\x85\xb1pXj\x9b\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\x1b\xf9t\u0650OE\u0381\xa8E\xe1\x1e\xf4\xcb\xcf'\xafq\x9e\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x1c\x04VI\xcdS\xdc#T\x1f\x8e\xd4\xd3A\x81(\b\xd5\u075c\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1c\x12\x8b\xd6\u0365\xfc\xa2uu\xe4\xb4;2S\xc8\xc4\x17*\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\x13\u04c67\xb9\xa4|\xe7\x9d7\xa8oP\xfb@\x9c\x06\a(\x89Hz\x9a0E9D\x00\x00\u07d4\x1c \x10\xbdf-\xf4\x17\xf2\xa2q\x87\x9a\xfb\x13\xefL\x88\xa3\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1c%z\u0525Q\x05\xea;X\xed7K\x19\x8d\xa2f\xc8_c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x1c.6\a\xe1'\xca\xca\x0f\xbd\\YH\xad\xad}\xd80\xb2\x85\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x1c5l\xfd\xb9_\xeb\xb7\x14c;(\xd5\xc12\u0744\xa9\xb46\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4\x1c5\xaa\xb6\x88\xa0\u034e\xf8.vT\x1b\xa7\xac9R\u007ft;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x1c>\xf0]\xae\x9d\xcb\u0509\xf3\x02D\bf\x9d\xe2D\xc5*\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x1cJ\xf0\xe8c\xd2el\x865\xbco\xfe\xc8\u0759(\x90\x8c\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c`\x19\x93x\x92\a\xf9e\xbb\x86\\\xbbL\xd6W\xcc\xe7o\xc0\x89\x05T\x1ap7P?\x00\x00\u07d4\x1cc\xfa\x9e,\xbb\xf2<I\xfc\xde\xf1\u02eb\xfen\r\x1e\x14\xc1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1cg\x02\xb3\xb0ZQ\x14\xbd\xbc\xae\xca%S\x1a\xee\xb3H5\xf4\x8a\x05\x85V\xbe\xadE\u072e\x00\x00\u07d4\x1ch\xa6a8x:c\u024c\xc6u\xa9\xecw\xafE\x98\xd3^\x89\x02\xb7F\xf4\x8f\x0f\x12\x00\x00\u07d4\x1cs\xd0\vn%\xd8\xeb\x9c\x1f\xf4\xad\x82{k\x9e\x9c\xf6\xd2\f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1cu\x1e\u007f$\u07dd\x94\xa67\xa5\xde\xde\xff\u0142w\xb5\xdb\x19\x89\xae\x8ez\v\xb5u\xd0\x00\x00\xe0\x94\x1c|\xb2\xfek\xf3\xe0\x9c\xbc\xdc\x18z\xf3\x8f\xa8\xf5\x05:p\xb6\x8a\x02\x1c\x84\xf7B\xd0\u03ad\x80\x00\xe0\x94\x1c\x89\x06\x0f\x98|Q\x8f\xa0y\xec,\n^\xbf\xa3\x0f] \xf7\x8a\b\v\xfb\xef\xcb_\v\xc0\x00\x00\u07d4\x1c\x94\xd66\xe6\x84\xeb\x15X\x95\xcem\xb4\xa2X\x8f\xba\x1d\x00\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\x99\xfe\x9b\xb6\xc6\xd1\x06m\x91 \x99T\u007f\xd1\U001007ac\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xb4P\x92\x00x\xaa\xb21|}\xb3\xb3\x8a\xf7\xdd)\x8b-A\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x1c\xb5\xf3;MH\x896\xd1>1a\xda3\xa1\xda}\xf7\r\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1c\xb6\xb2\xd7\xcf\xc5Y\xb7\xf4\x1eoV\xab\x95\xc7\xc9X\xcd\x0eL\x89Hz\x9a0E9D\x00\x00\u07d4\x1c\xc1\xd3\xc1O\x0f\xb8d\x0e6rM\xc42)\xd2\xeaz\x1eH\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x1c\xc9\bv\x00A\t\xcdy\xa3\u07a8f\u02c4\n\xc3d\xba\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xd1\xf0\xa3\x14\u02f2\x00\xde\n\f\xb1\xef\x97\xe9 p\x9d\x97\u0089lk\x93[\x8b\xbd@\x00\x00\u0794\x1c\xdaA\x1b\xd5\x16;\xae\xca\x1eU\x85c`\x1c\xe7 \xe2N\xe1\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1c\xe8\x1d1\xa7\x920\"\xe1%\xbfH\xa3\xe06\x93\xb9\x8d\xc9\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xeb\xf0\x98]\u007fh\n\xaa\x91\\D\xccb\xed\xb4\x9e\xab&\x9e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1c\xedg\x15\xf8b\xb1\xff\x86\x05\x82\x01\xfc\xceP\x82\xb3nb\xb2\x8a\x01j^`\xbe\xe2s\xb1\x00\x00\u07d4\x1c\xf0L\xb1C\x80\x05\x9e\xfd?#\x8be\u057e\xb8j\xfa\x14\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1c\xf1\x05\xab#\x02;ULX>\x86\u05d2\x11y\xee\x83\x16\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1c\xf2\xebz\x8c\xca\u00ad\xea\xef\x0e\xe8sG\xd55\u04f9@X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1c\xfc\xf7Q\u007f\f\bE\x97 \x94+dz\u0452\xaa\x9c\x88(\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x1d\t\xad$\x12i\x1c\u0141\xc1\xab6\xb6\xf9CL\xd4\xf0\x8bT\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\x1d\x15|Xv\xc5\xca\xd5S\xc9\x12\xca\xf6\xce-Rw\xe0\\s\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x1d&\x15\xf8\xb6\xcaP\x12\xb6c\xbd\u0414\xb0\xc5\x13|w\x8d\u07ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1d)\u01ea\xb4+ H\u04b2R%\u0518\u06e6z\x03\xfb\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794\x1d4\x1f\xa5\xa3\xa1\xbd\x05\x1f}\xb8\a\xb6\xdb/\u01faO\x9bE\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1d4N\x96%g\xcb'\xe4M\xb9\xf2\xfa\u01f6\x8d\xf1\xc1\xe6\xf7\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x1d6h0c\xb7\xe9\xeb\x99F-\xab\xd5i\xbd\xdc\xe7\x16\x86\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d7aky?\x94\x91\x188\xac\x8e\x19\xee\x94I\u07d2\x1e\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94\x1d9[0\xad\xda\x1c\xf2\x1f\t\x1aOJ{u3q\x18\x94A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x1dEXn\xb8\x03\xca!\x90e\v\xf7H\xa2\xb1t1+\xb5\a\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1dW.\xdd-\x87\xca'\x1ag\x14\xc1Z;7v\x1d\u0320\x05\x89\x06\xeb\xd5*\x8d\xdd9\x00\x00\u07d4\x1dc0\x97\xa8R%\xa1\xffC!\xb1)\x88\xfd\xd5\\+8D\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x1di\xc8=(\xff\x04t\xce\xeb\xea\xcb:\xd2'\xa1D\xec\u78ca\x01(\xcc\x03\x92\nb\u0480\x00\u07d4\x1d\x96\xbc\u0544W\xbb\xf1\xd3\u00a4o\xfa\xf1m\xbf}\x83hY\x89\tIr\t\xd8F~\x80\x00\u07d4\x1d\x9ej\xaf\x80\x19\xa0_#\x0e]\xef\x05\xaf]\x88\x9b\xd4\xd0\xf2\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x1d\xab\x17.\xff\xa6\xfb\xeeSL\x94\xb1~yN\xda\xc5OU\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x1d\xb9\xac\x9a\x9e\xae\xec\nR7W\x05\fq\xf4rx\xc7-P\x89Hz\x9a0E9D\x00\x00\u07d4\x1d\xbe\x8e\x1c+\x8a\x00\x9f\x85\xf1\xad<\xe8\r.\x055\x0e\u3709\aW\rn\x9e\xbb\xe4\x00\x00\u07d4\x1d\xc7\xf7\xda\xd8]\xf5?\x12q\x15$\x03\xf4\xe1\xe4\xfd\xb3\xaf\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1d\u03bc\xb7em\xf5\u072a3h\xa0U\xd2/\x9e\xd6\xcd\xd9@\x89\x1b\x18\x1eK\xf24<\x00\x00\xe0\x94\x1d\xd7tA\x84J\xfe\x9c\xc1\x8f\x15\xd8\xc7{\xcc\xfbe^\xe04\x8a\x01\x06\xebEW\x99D\x88\x00\x00\u07d4\x1d\xde\xfe\xfd5\xab\x8fe\x8b$q\xe5G\x90\xbc\x17\xaf\x98\u07a4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1d\xee\xc0\x1a\xbe\\\r\x95-\xe9\x10l=\xc3\x069\xd8P\x05\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1d\xf6\x91\x16rg\x9b\xb0\xef5\t\x03\x8c\f'\xe3\x94\xfd\xfe0\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\x1d\xfa\xee\ar\x12\xf1\xbe\xaf\x0eo/\x18@Sz\xe1T\xad\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x1e\x06\r\xc6\xc5\xf1\u02cc\xc7\xe1E.\x02\xee\x16u\b\xb5eB\x8a\x02\xb1O\x02\xc8d\xc7~\x00\x00\xe0\x94\x1e\x13\xecQ\x14,\ubde2`\x83A,<\xe3QD\xbaV\xa1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x1e\x1aH(\x11\x9b\xe3\t\xbd\x88#nMH+PM\xc5W\x11\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x1e\x1a\ud178leb\u02cf\xa1\xebo\x8f;\xc9\u072eny\x89\xf4\xd2\u0744%\x9b$\x00\x00\u07d4\x1e\x1ccQwj\xc3\x10\x919~\xcf\x16\x00-\x97\x9a\x1b-Q\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x1e\x1dz_$h\xb9N\xa8&\x98-\xbf!%y<nJZ\x8964\xf4\x84\x17@\x1a\x00\x00\u07d4\x1e!\x0epG\x88m\xaaR\xaa\xf7\x0fK\x99\x1d\xach\xe3\x02^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1e+\xf4\xba\x8e^\xf1\x8d7\xdemj\xd66\xc4\xca\xe4\x89\xd0\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e/\xe4\xe4\xa7}\x14\x1f\xf4\x9a\f\u007f\xbc\x95\xb0\xa2\xb2\x83\xee\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e3\xd1\xc2\xfb^\bO/\x1dT\xbcRgr\u007f\xec?\x98]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x1e8\x1a\xdc\xf8\x01\xa3\xbf\x9f\u05ff\xac\x9c\xcc+\x84\x82\xad^f\x89 \x89r\xc0\x01\rt\x00\x00\xe0\x94\x1e;\xad\xb1\xb6\xe18\x0e'\x03\x9cWj\xe6\".\x96:[S\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x1eHM\x06!\xf0\xf53\x1b5\xd5@\x8d\x9a\xaeN\xb1\xac\xf2\x1e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1eX\x00\"}M\xcfu\xe3\x0fU\x95\u017e\xd3\xf7.4\x1e;\x89\ru\xda\xcesA~\x00\x00\u07d4\x1eYj\x81\xb3W\xc6\xf2Ip\xcc1=\xf6\xdb\u06ab\xd0\u041e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1ei\x15\xeb\u0661\x9c\x81\xb6\x92\xad\x99\xb1!\x8aY,\x1a\u01f1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x1en\x01S\xfc\x16\x1b\xc0^ek\xbb\x14Lq\x87\xbfO\xe8M\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1epfU\xe2\x84\xdc\xf0\xbb7\xfe\a]a:\x18\xdc\x12\xffJ\x89\xedC\xbf\x1e\ue0ac\x00\x00\xe0\x94\x1ex>R*\xb7\xdf\n\u02ac\x9e\xee\xd3Y09\xe5\xacuy\x8a+\x14F\xddj\xef\xe4\x1c\x00\x00\u07d4\x1e{^M\x1fW+\xec\xf2\xc0\x0f\xc9\f\xb4v{Jn3\u0509\x06\x1f\xc6\x10u\x93\xe1\x00\x00\u07d4\x1e\x8eh\x9b\x02\x91|\xdc)$]\f\x9ch\xb0\x94\xb4\x1a\x9e\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xa34\xb5u\b\a\xeat\xaa\u016b\x86\x94\xec_(\xaaw\u03c9\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x1e\xa4qU\x04\u01af\x10{\x01\x94\xf4\xf7\xb1\xcbo\xcc\xcdoK\x89 \x041\x97\xe0\xb0'\x00\x00\u07d4\x1e\xa4\x92\xbc\xe1\xad\x10~3\u007fK\u0527\xac\x9a{\xab\xcc\u036b\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x1e\xa6\xbf/\x15\xae\x9c\x1d\xbcd\u06a7\xf8\xeaM\r\x81\xaa\xd3\xeb\x89\u3bb5sr@\xa0\x00\x00\u07d4\x1e\xb4\xbfs\x15j\x82\xa0\xa6\x82 \x80\xc6\xed\xf4\x9cF\x9a\xf8\xb9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\x1e\xba\xcbxD\xfd\xc3\"\xf8\x05\x90O\xbf\x19b\x80-\xb1S|\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x1e\xc4\xecKw\xbf\x19\u0411\xa8h\xe6\xf4\x91T\x18\x05A\xf9\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xd0n\xe5\x16b\xa8lcE\x88\xfbb\xdcC\xc8\xf2~|\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1e\u063b?\x06w\x8b\x03\x9e\x99a\xd8\x1c\xb7\x1as\xe6x|\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x1e\xda\bNye\x00\xba\x14\xc5\x12\x1c\r\x90\x84of\xe4\xbeb\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4\x1e\xeel\xbe\xe4\xfe\x96\xadaZ\x9c\xf5\x85zdy@\u07ccx\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4\x1e\xf2\u073f\xe0\xa5\x00A\x1d\x95n\xb8\u0213\x9c=l\xfef\x9d\x89*\x11)\u0413g \x00\x00\xe0\x94\x1e\xf5\xc9\xc76P\u03fb\xde\\\x88U1\xd4'\xc7\xc3\xfeUD\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x1f\x04\x12\xbf\xed\u0356N\x83}\t,q\xa5\xfc\xba\xf3\x01&\xe2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x17O@\xa0Dr4\xe6fS\x91Mu\xbc\x00>V\x90\u0709\b\xacr0H\x9e\x80\x00\x00\u07d4\x1f!\x86\xde\xd2>\f\xf9R\x16\x94\xe4\xe1dY>i\n\x96\x85\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x1f*\xfc\n\xed\x11\xbf\xc7\x1ew\xa9\ae{6\xeav\xe3\xfb\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x1f9Y\xfc)\x11\x10\xe8\x822\xc3kvg\xfcx\xa3ya?\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x1f=\xa6\x8f\xe8~\xafC\xa8)\xabm~\u0166\xe0\t\xb2\x04\xfb\x89\x1e\x16\x01u\x8c,~\x00\x00\u07d4\x1fI\xb8m\r9EY\x06\x98\xa6\xaa\xf1g<7u\\\xa8\r\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\x1f_;4\xbd\x13K'\x81\xaf\xe5\xa0BJ\u0144l\xde\xfd\x11\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\u07d4\x1fo\x0004\x97R\x06\x1c\x96\a+\xc3\xd6\xeb5I \x8dk\x89\x01K\x8d\xe1\xeb\x88\u06c0\x00\u07d4\x1f}\x8e\x86\xd6\xee\xb0%E\xaa\xd9\x0e\x912{\xd3i\xd7\xd2\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\x81\x16\xbd\n\xf5W\x0e\xaf\fV\u011cz\xb5\xe3zX\x04X\x89lk\x93[\x8b\xbd@\x00\x00\u0794\x1f\x88\xf8\xa13\x8f\xc7\xc1\tv\xab\xcd?\xb8\u04c5T\xb5\uc708\xb9\xf6]\x00\xf6<\x00\x00\u07d4\x1f\x9c2hE\x8d\xa3\x01\xa2\xbeZ\xb0\x82W\xf7{\xb5\xa9\x8a\xa4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x1f\xa21\x9f\xed\x8c-F*\xdf.\x17\xfe\xecjo0Qn\x95\x89\x06\xca\xe3\x06!\xd4r\x00\x00\u07d4\x1f\xb4c\xa08\x99\x83\xdf}Y?{\xddmxI\u007f\xed\x88y\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x1f\xb7\xbd1\r\x95\xf2\xa6\u067a\xaf\x8a\x8aC\n\x9a\x04E:\x8b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x1f\xcc|\xe6\xa8HX\x95\xa3\x19\x9e\x16H\x1fr\xe1\xf7b\xde\xfe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x1f\xcf\xd1\xd5\u007f\x87\"\x90V\f\xb6-`\x0e\x1d\xef\xbe\xfc\xcc\x1c\x89P\xc5\xe7a\xa4D\b\x00\x00\u0794\x1f\u0496\xbe\x03\xads|\x92\xf9\u0186\x9e\x8d\x80\xa7\x1cW\x14\xaa\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x1f\xdd\xd8_\u024b\xe9\xc4\x04Ya\xf4\x0f\x93\x80^\xccEI\xe5\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4 \x01\xbe\xf7{f\xf5\x1e\x15\x99\xb0/\xb1\x10\x19J\x00\x99\xb7\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \x02d\xa0\x9f\x8ch\xe3\xe6b\x97\x95(\x0fV%O\x86@\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x03qy\a\xa7%`\xf40\u007f\x1b\xee\xccT6\xf4=!\xe7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \r\xfc\vq\xe3Y\xb2\xb4eD\n6\xa6\xcd\xc3Rw0\a\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4 \x13L\xbf\xf8\x8b\xfa\xdcFkR\xec\ua9d8W\x89\x1d\x83\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4 \x14&\x1f\x01\b\x9fSyV0\xba\x9d\xd2O\x9a4\xc2\xd9B\x89Hz\x9a0E9D\x00\x00\u07d4 \x16\x89]\xf3,\x8e\xd5G\x82iF\x84#\xae\xa7\xb7\xfb\xceP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \x18\x1cKA\xf6\xf9r\xb6iX!_\x19\xf5p\xc1]\xdf\xf1\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4 \x18d\xa8\xf7\x84\xc2'{\v|\x9e\xe74\xf7\xb3w\xea\xb6H\x89\xf2(\x14\x00\xd1\xd5\xec\x00\x00\u07d4 \xb8\x1a\xe59&\xac\xe9\xf7\xd7AZ\x05\f\x03\x1dX_ \x89\x12\u007f\x19\xe8>\xb3H\x00\x00\xe0\x94 <b\x83\xf2\r\xf7\xbc\x86T/\u07f4\xe7c\xec\u06fb\xee\xf5\x8a\x05K@\xb1\xf8R\xbd\xa0\x00\x00\xe0\x94 J\u0248g\xa7\xc9\xc7\xedq\x1c\xb8/(\xa8x\xca\xf6\x9bH\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4 R7\u013e\x14o\xba\x99G\x8f:}\xad\x17\xb0\x918\u0695\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 S\xac\x97T\x8a\fN\x8b\x80\xbcrY\f\u05a0\x98\xfeu\x16\x89\n#%u;F\f\x00\x00\u07d4 _Qf\xf1$@\xd8Wb\xc9g\u04ee\x86\x18O\x8fM\x98\x89\x17r$\xaa\x84Lr\x00\x00\xe0\x94 _\xc8C\xe1\x9aI\x13\u0448\x1e\xb6\x9bi\xc0\xfa;\xe5\xc5\v\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4 d\x82\xeeo\x13\x8aw\x8f\xe1\xadb\xb1\x80\u0385o\xbb#\xe6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94 fwM\x82'\x93\xff%\xf1v\t\tG\x9c\xf6$\x91\xbf\x88\x8a\v\xae:\u0185\xcbr\xe0\x00\x00\u07d4 mU\xd5y*QN\xc1\b\xe0\x90Y\x9f*\x06^P\x11\x85\x89\n\xdf0\xbap\u0217\x00\x00\u07d4 p~B]*\x11\xd2\u021f9\x1b+\x80\x9fUlY$!\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94 ~\xf8\v]`\xb6\xfb\xff\xc5\x1f:d\xb8\xc7 6\xa5\xab\xbd\x8a\x01je\x02\xf1Z\x1eT\x00\x00\xe0\x94 \x82K\xa1\xdb\xeb\xbe\xf9\x84n\xf3\xd0\xf6\xc1\xb0\x17\xe6\x91.\u010a\x01\x84\xb2nM\xaf\x1d5\x00\x00\u07d4 \x84\xfc\xe5\x05\xd9{\xeb\xf1\xad\x8c_\xf6\x82o\xc6E7\x1f\xb2\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4 \x8cEs,\n7\x8f\x17\xac\x83$\x92mE\x9b\xa8\xb6X\xb4\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4 \x93w\xb6\xad?\xe1\x01\xc9h[5vT\\k\x16\x84\xe7<\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4 \x9e\x8e)\xd3;\xea\xe8\xfbk\xaax=\x13>\x1d\x9e\xc1\xbc\v\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4 \xa1RV\xd5\f\xe0X\xbf\x0e\xacC\xaaS:\xa1n\u0273\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4 \xa2\x9cPy\xe2k?\x181\x8b\xb2\xe5\x0e\x8e\x8b4n[\xe8\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4 \xa8\x16\x80\xe4e\xf8\x87\x90\xf0\aO`\xb4\xf3_]\x1ej\xa5\x89Ea\x80'\x8f\fw\x80\x00\u07d4 \xb9\xa9\u6f48\x80\u0659J\xe0\r\u0439(*\v\xea\xb8\x16\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4 \u0084\xba\x10\xa2\b0\xfc=i\x9e\xc9}-\xfa'\xe1\xb9^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4 \xd1A\u007f\x99\xc5i\u3fb0\x95\x85e0\xfe\x12\xd0\xfc\uaa89@\x15\xf9K\x11\x83i\x80\x00\u07d4 \u074f\u02f4n\xa4o\u3066\x8b\x8c\xa0\xea[\xe2\x1f\u9949lk\x93[\x8b\xbd@\x00\x00\xe0\x94 \xff>\u078c\xad\xb5\xc3{H\xcb\x14X\x0f\xb6^#\t\n{\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\xe0\x94!\x008\x1d`\xa5\xb5J\xdc\t\u0456\x83\xa8\xf6\u057bK\xfb\u02ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x18\xc1\x16\xab\f\xdfo\xd1\x1dT\xa40\x93\a\xb4w\xc3\xfc\x0f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94!\x1b)\xce\xfcy\xae\x97gD\xfd\xeb\u03bd<\xbb2\xc5\x13\x03\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4! l\xe2.\xa4\x80\xe8Y@\xd3\x13\x14\xe0\xd6ONM:\x04\x8965\u026d\xc5\u07a0\x00\x00\u07d4!2\xc0Qj.\x17\x17J\xc5G\xc4;{\x00 \xd1\xebLY\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94!@\x8bMz,\x0en\xcaAC\xf2\xca\u037b\u033a\x12\x1b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d4!Kt9U\xa5\x12\xden\r\x88j\x8c\xbd\x02\x82\xbe\xe6\u04a2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!L\x89\u017d\x8e}\"\xbcWK\xb3^H\x95\x02\x11\xc6\xf7v\x89\x01\x06T\xf2X\xfd5\x80\x00\xe0\x94!Ti\x14\xdf\u04ef*\xddA\xb0\xff>\x83\xff\xdat\x14\xe1\xe0\x8a\x01C\x95\xe78ZP.\x00\x00\u07d4!X.\x99\xe5\x02\xcb\xf3\xd3\xc2;\xdf\xfbv\xe9\x01\xacmV\xb2\x89\x05k\xc7^-c\x10\x00\x00\u07d4!Y$\b\x13\xa70\x95\xa7\xeb\xf7\u00f3t>\x80(\xae_\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!`\xb4\xc0,\xac\n\x81\u0791\b\xdeCE\x90\xa8\xbf\xe6\x875\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94!nA\x86N\xf9\x8f\x06\r\xa0\x8e\xca\xe1\x9a\xd1\x16j\x17\xd06\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4!\x84o/\xdfZA\xed\x8d\xf3n^\xd8TM\xf7Y\x88\xec\xe3\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94!\xa6\xdbe'F{\xc6\xda\xd5K\xc1n\x9f\xe2\x95;g\x94\xed\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4!\xa6\xfe\xb6\xab\x11\xc7f\xfd\xd9w\xf8\xdfA!\x15_G\xa1\xc0\x89\x03\x19\xcf8\xf1\x00X\x00\x00\u07d4!\xb1\x82\xf2\xda+8D\x93\xcf_5\xf8=\x9d\x1e\xe1O*!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xbf\xe1\xb4\\\xac\xdebt\xfd\x86\b\u0661x\xbf>\xebn\u0709l\xee\x06\u077e\x15\xec\x00\x00\u07d4!\xc0s\x80HOl\xbc\x87$\xad2\xbc\x86L;Z\xd5\x00\xb7\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94!\u00e8\xbb\xa2g\xc8\u0322{\x1a\x9a\xfa\xba\xd8o`z\xf7\b\x8a\x01\xe4\xa3lI\u06580\x00\x00\u07d4!\xcem[\x90\x18\xce\xc0J\u0596yD\xbe\xa3\x9e\x800\xb6\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4!\xd0'\x05\xf3\xf6I\x05\xd8\x0e\xd9\x14y\x13\xea\x8cs\a\u0595\x89I\xed\xb1\xc0\x98\x876\x00\x00\u07d4!\xd1?\f@$\xe9g\xd9G\a\x91\xb5\x0f\"\xde:\xfe\xcf\x1b\x89\xf1Z\xd3^.1\xe5\x00\x00\xe0\x94!\xdb\u06c1z\r\x84\x04\u01bd\xd6\x15\x047N\x9cC\xc9!\x0e\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\xe0\x94!\xdf\x1e\xc2KNK\xfey\xb0\xc0\x95\u03ba\xe1\x98\xf2\x91\xfb\u044a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94!\xdf-\u036ft\xb2\xbf\x804\x04\xddM\xe6\xa3^\xab\xec\x1b\xbd\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4!\xe2\x19\u021c\xa8\xac\x14\xaeL\xbaa0\xee\xb7}\x9em9b\x89*\u035f\xaa\xa08\xee\x00\x00\u07d4!\xe5\u04ba\xe9\x95\xcc\xfd\b\xa5\xc1k\xb5$\xe1\xf60D\x8f\x82\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4!\xe5\xd7s 0L \x1c\x1eS\xb2a\xa1#\u0421\x06>\x81\x89\x04\xb6\xfa\x9d3\xddF\x00\x00\xe0\x94!\xea\xe6\xfe\xff\xa9\xfb\xf4\u0347OG9\xac\xe50\u033eY7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4!\xec\xb2\u07e6Wy\xc7Y-\x04\x1c\xd2\x10Z\x81\xf4\xfdNF\x8965\u026d\xc5\u07a0\x00\x00\u07d4!\uff20\x9b5\x80\xb9\x8es\xf5\xb2\xf7\xf4\xdc\v\xf0,R\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4!\xfd\v\xad\xe5\xf4\xeftt\xd0X\xb7\xf3\xd8T\xcb\x13\x00RN\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94!\xfdG\xc5%`\x12\x19\x8f\xa5\xab\xf11\xc0mj\xa1\x96_u\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4!\xfdl]\x97\xf9\xc6\x00\xb7h!\xdd\xd4\xe7v5\x0f\xce+\xe0\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\"\r\u018d\xf0\x19\xb6\xb0\u033f\xfbxKZZ\xb4\xb1]@`\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\"\x0e+\x92\xc0\xf6\xc9\x02\xb5\x13\xd9\xf1\xe6\xfa\xb6\xa8\xb0\xde\xf3\u05c9+^:\xf1k\x18\x80\x00\x00\u07d4\"V\x1cY1\x14560\x9c\x17\xe82X{b\\9\v\x9a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"W\xfc\xa1jn\\*d|<)\xf3l\xe2)\xab\x93\xb1~\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\"]5\xfa\xed\xb3\x91\u01fc-\xb7\xfa\x90q\x16\x04\x05\x99m\x00\x89\t\x18T\xfc\x18bc\x00\x00\u07d4\"_\x9e\xb3\xfbo\xf3\xe9\xe3\xc8D~\x14\xa6n\x8dO7y\xf6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\"r\x18n\xf2}\xcb\xe2\xf5\xfc70P\xfd\xae\u007f*\xce#\x16\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4\"s\xba\u05fcNHv\"\xd1u\xefzf\x98\x8bj\x93\xc4\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\"v&K\xec\x85&\xc0\xc0\xf2pgz\xba\xf4\xf0\xe4A\xe1g\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\"\x82B\xf83n\xec\xd8$.\x1f\x00\x0fA\x93~q\xdf\xfb\xbf\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\"\x84*\xb80\xdaP\x99\x13\xf8\x1d\xd1\xf0O\x10\xaf\x9e\xdd\x1cU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x94O\xbc\xa9\xb5yc\bN\xb8M\xf7\xc8_\xb9\xbc\u07f8V\x89\xfc\x11\x8f\uf43a8\x80\x00\u07d4\"\x9c\xc4q\x1bbu^\xa2\x96DZ\u00f7\u007f\xc63\x82\x1c\xf2\x89\x02#\xe8\xb0R\x192\x80\x00\u0794\"\x9eC\r\xe2\xb7OD&Q\xdd\u0377\x01v\xbc\x05L\xadT\x88\xbb\xf9\x81\xbcJ\xaa\x80\x00\u07d4\"\x9fO\x1a*OT\atP[G\a\xa8\x1d\xe4D\x10%[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\"\x9f\xf8\v\xf5p\x80\t\xa9\xf79\xe0\xf8\xb5`\x91@\x16\u0566\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4\"\xa2X\x12\xabV\xdc\xc4#\x17^\xd1\u062d\xac\xce3\xcd\x18\x10\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\"\xb9j\xb2\xca\xd5]\xb1\x00\xb50\x01\xf9\xe4\xdb7\x81\x04\xc8\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\"\xbd\xff\xc2@\xa8\x8f\xf7C\x1a\xf3\xbf\xf5\x0e\x14\xda7\xd5\x18>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xce4\x91Y\xee\xb1D\xef\x06\xff&6X\x8a\xefy\xf6(2\x89\n1\x06+\xee\xedp\x00\x00\u07d4\"\xdbU\x9f,<\x14u\xa2\xe6\xff\xe8:YyY\x91\x96\xa7\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\"\xe1QX\xb5\xee>\x86\xeb\x032\xe3\u6a6cl\u0675^\u0349\b\xacr0H\x9e\x80\x00\x00\u07d4\"\xe2H\x8e-\xa2jI\xae\x84\xc0\x1b\xd5K!\xf2\x94x\x91\u0189]\u0212\xaa\x111\xc8\x00\x00\u07d4\"\xe5\x12\x14\x9a\x18\xd3i\xb7<q\xef\xa4>\x86\xc9\xed\xab\xaf\x1d\x89N\xe0.g\x14a\\\x00\x00\u07d4\"\xeb}\xb0\xbaV\xb0\xf8\xb8\x16\u0332\x06\xe6\x15\xd9)\x18[\r\x89\x04])s~\"\xf2\x00\x00\u07d4\"\xee\xd3'\xf8\xeb\x1d\x138\xa3\xcb{\x0f\x8aK\xaaY\a\u0355\x89\x01E]_Hw\b\x80\x00\xe0\x94\"\xf0\x04\u07cd\xe9\xe6\xeb\xf5#\u032c\xe4W\xac\xcb&\xf9r\x81\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\"\xf2\xdc\xffZ\u05cc>\xb6\x85\v\\\xb9Q\x12{e\x95\"\u623e -j\x0e\xda\x00\x00\u07d4\"\xf3\xc7y\xddy\x02>\xa9*x\xb6\\\x1a\x17\x80\xf6-\\J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\"\xfe\x88M\x907)\x1bMR\xe6(Z\xe6\x8d\xea\v\xe9\xff\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x06\u07d3\x1a\x94\rX\xc0\x16e\xfaM\b\x00\x80,\x02\xed\xfe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94#\t\xd3@\x91D[22Y\v\xd7\x0fO\x10\x02[,\x95\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#\x12\x00F\xf6\x83!\x02\xa7R\xa7fVi\x1c\x86>\x17\u5709\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4#\x1a\x15\xac\xc1\x99\u021f\xa9\xcb\"D\x1c\xc7\x030\xbd\xcc\xe6\x17\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4#\x1d\x94\x15]\xbc\xfe*\x93\xa3\x19\xb6\x17\x1fc\xb2\v\u04b6\xfa\x89\xcf\x14{\xb9\x06\xe2\xf8\x00\x00\u07d4#(2\xcdYw\xe0\nL0\xd0\x16?.$\xf0\x88\xa6\xcb\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4#,m\x03\xb5\xb6\xe6q\x1e\xff\xf1\x90\xe4\x9c(\xee\xf3l\x82\xb0\x89Hz\x9a0E9D\x00\x00\xe0\x94#,\xb1\xcdI\x99<\x14J?\x88\xb3a\x1e#5i\xa8k\u058a\x03L`lB\u042c`\x00\x00\u07d4#,\xe7\x82Pb%\xfd\x98`\xa2\xed\xc1Jz0Gsm\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4#/R]U\x85\x9b}N`\x8d H\u007f\xaa\xdb\x00)15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94#4\u0150\u01e4\x87i\x100E\u0176SL\x8a4i\xf4J\x8a\x03\xb1\x99\a=\xf7-\xc0\x00\x00\u07d4#7n\u02bftl\xe53!\xcfB\xc8fI\xb9+g\xb2\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#7\x8fB\x92m\x01\x84\xb7\x93\xb0\xc8'\xa6\xdd>=3O\u0349\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4#8B\xb1\xd0i/\xd1\x11@\xcfZ\u0364\xbf\x960\xba\xe5\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#9\xe9I(p\xaf\xea%7\xf3\x89\xac/\x83\x83\x02\xa3<\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#;\xdd\xdd]\xa9HR\xf4\xad\xe8\xd2\x12\x88V\x82\xd9\ak\u0189\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#OF\xba\xb7?\xe4]1\xbf\x87\xf0\xa1\xe0Fa\x99\xf2\ubb09\x1aJ\xba\"\\ t\x00\x00\u07d4#U\x1fV\x97_\xe9+1\xfaF\x9cI\xeaf\xeefb\xf4\x1e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4#V\x95B\xc9}V`\x18\xc9\a\xac\xfc\xf3\x91\xd1@g\xe8~\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94#_\xa6l\x02^\xf5T\x00p\xeb\xcf\r7-\x81w\xc4g\xab\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\xe0\x94#r\xc4\xc1\u0253\x9fz\xafl\xfa\xc0@\x90\xf0\x04t\x84\n\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4#s\f5z\x91\x02nD\xb1\xd0\xe2\xfc*Q\xd0q\xd8\xd7{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#v\xad\xa9\x033\xb1\u0441\bL\x97\xe6E\xe8\x10\xaa[v\xf1\x89(\xa8WBTf\xf8\x00\x00\u07d4#x\xfdC\x82Q\x1e\x96\x8e\u0452\x10g7\xd3$\xf4T\xb55\x8965\u026d\xc5\u07a0\x00\x00\u07d4#\x82\xa9\u050e\xc8>\xa3e(\x90\xfd\x0e\u7710{[-\xc1\x89\a?u\u0460\x85\xba\x00\x00\u07d4#\x83\xc2\"\xe6~\x96\x91\x90\xd3!\x9e\xf1M\xa3xP\xe2lU\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\x8akv5%/RDHl\n\xf0\xa7: s\x85\xe09\x89JD\x91\xbdm\xcd(\x00\x00\u07d4#\x9as>k\x85Z\u0152\xd6c\x15a\x86\xa8\xa1t\xd2D\x9e\x89X\xbe7X\xb2A\xf6\x00\x00\xe0\x94#\xab\t\xe7?\x87\xaa\x0f;\xe0\x13\x9d\xf0\xc8\xebk\xe5cO\x95\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94#\xab\xd9\xe9>yW\xe5\xb66\xbeey\x05\x1c\x15\xe5\xce\v\x0e\x8a\x03\xa3\xc8\xf7\xcb\xf4,8\x00\x00\u07d4#\xb1\u0111\u007f\xbd\x93\xee=H8\x93\x06\x95s\x84\xa5Il\xbf\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94#\xba8d\xdaX=\xabV\xf4 \x87<7g\x96\x90\xe0/\x00\x8a\x02\x13BR\r_\xec \x00\x00\u07d4#\xc5Z\xebW9\x87o\n\xc8\xd7\xeb\xea\x13\xber\x96\x85\xf0\x00\x89Hz\x9a0E9D\x00\x00\u07d4#\u025b\xa0\x87D\x8e\x19\xc9p\x1d\xf6n\f\xabR6\x831\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xcc\xc3\u01ac\xd8\\.F\fO\xfd\xd8+\xc7]\xc8I\xea\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4#\xcd%\x98\xa2\x0e\x14\x9e\xad*\u0593yWn\xce\xdb`\u3389lk\x93[\x8b\xbd@\x00\x00\u07d4#\u07cfH\xee\x00\x92V\xeay~\x1f\xa3i\xbe\xeb\xcfk\xc6c\x89|\xd3\xfa\xc2m\x19\x81\x80\x00\u07d4#\xe2\u01a8\xbe\x8e\n\u03e5\xc4\xdf^6\x05\x8b\xb7\u02ecZ\x81\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xeaf\x9e5d\x81\x9a\x83\xb0\xc2l\x00\xa1m\x9e\x82olF\x89M\x8dl\xa9h\xca\x13\x00\x00\u07d4#\xebo\xd8Vq\xa9\x06:\xb7g\x8e\xbe&Z \xf6\x1a\x02\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4#\xf9\xec\xf3\xe5\xdd\u0723\x88\x15\xd3\xe5\x9e\xd3K[\x90\xb4\xa3S\x89\v\x17\x81\xa3\xf0\xbb \x00\x00\u07d4#\xfa~\xb5\x1aH\"\x95\x98\xf9~v+\xe0\x86\x96R\xdf\xfcf\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94$\x03\x05rs\x13\xd0\x1esT,w_\xf5\x9d\x11\xcd5\xf8\x19\x8a\x01A\x88Vf\x80\u007f\\\x80\x00\u07d4$\x04k\x91\u069ba\xb6)\u02cb\x8e\xc0\xc3Q\xa0~\a\x03\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\x0eU\x9e'J\xae\xf0\xc2X\x99\x8c\x97\x9fg\x1d\x11s\xb8\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94$\x13aU\x9f\xee\xf8\x0e\xf170!S\xbd\x9e\xd2\xf2]\xb3\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94$;;\xcaj)\x93Y\xe8\x86\xce3\xa3\x03A\xfa\xfeMW=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4$<\x84\xd1$ W\f\xc4\xef;\xab\xa1\xc9Y\u0083$\x95 \x89\u007f\x1fi\x93\xa8S\x04\x00\x00\xe0\x94$CJ>2\xe5N\xcf'/\xe3G\v_oQ/gU \x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4$HYo\x91\xc0\x9b\xaa0\xbc\x96\x10j-7\xb5p^](\x89lk\x93[\x8b\xbd@\x00\x00\u0794$Xn\xc5E\x175\xee\xaa\xebG\r\xc8sj\xaeu/\x82\xe5\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4$X\xd6U_\xf9\x8a\x12\x9c\xce@7\x95=\x00 n\xffB\x87\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4$b\x91\x16[Y3-\xf5\xf1\x8c\xe5\u0248V\xfa\xe9X\x97\u0589\\(=A\x03\x94\x10\x00\x00\u07d4$g\u01a5\u0196\xed\xe9\xa1\xe5B\xbf\x1a\xd0k\xccK\x06\xac\xa0\x89\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4$v\xb2\xbbu\x1c\xe7H\xe1\xa4\xc4\xff{#\v\xe0\xc1]\"E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4$z\n\x11\xc5\u007f\x03\x83\xb9I\xdeT\vf\xde\xe6\x86\x04\xb0\xa1\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4$\x87\xc3\u013e\x86\xa2r=\x91|\x06\xb4XU\x01p\xc3\xed\xba\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x89\xac\x12i4\xd4\u05a9M\xf0\x87C\xda{v\x91\xe9y\x8e\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\x9d\xb2\x9d\xbc\x19\xd1#]\xa7)\x8a\x04\b\x1c1WB\u9b09a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4$\xa4\xeb6\xa7\xe4\x98\xc3o\x99\x97\\\x1a\x8dr\x9f\u05b3\x05\u05c9\r\xfcx!\x0e\xb2\xc8\x00\x00\u07d4$\xa7P\xea\xe5\x87G\x11\x11m\xd7\xd4{q\x86\u0399\r1\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xaa\x11Q\xbbv_\xa3\xa8\x9c\xa5\x0e\xb6\xe1\xb1\xc7\x06A\u007f\u0509\xa8\r$g~\xfe\xf0\x00\x00\u0794$\xac\xa0\x8d[\xe8^\xbb\x9f12\xdf\xc1\xb6 \x82N\xdf\xed\xf9\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4$\xb2\xbe\x11\x8b\x16\u0632\x17Gi\xd1{L\xf8O\a\u0294m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4$\xb8\xb4F\u07bd\x19G\x95]\u0404\xf2\xc5D\x933F\u04ed\x89\xeaim\x90@9\xbd\x80\x00\u07d4$\xb9^\xbe\xf7\x95\x00\xba\xa0\xed\xa7.w\xf8wA]\xf7\\3\x891T\xc9r\x9d\x05x\x00\x00\u07d4$\xb9\xe6dOk\xa4\xcd\xe1&'\r\x81\xf6\xab`\xf2\x86\xdf\xf4\x89\a?u\u0460\x85\xba\x00\x00\u07d4$\xbdY\x04\x05\x90\x91\xd2\xf9\xe1-j&\xa0\x10\xca\"\xab\x14\xe8\x89e\xea=\xb7UF`\x00\x00\u07d4$\xc0\u020bT\xa3TG\t\x82\x8a\xb4\xab\x06\x84\x05Y\xf6\xc5\u2250\xf54`\x8ar\x88\x00\x00\u07d4$\xc1\x17\xd1\u04b3\xa9z\xb1\x1aFy\u025awJ\x9e\xad\xe8\u044965\u026d\xc5\u07a0\x00\x00\u07d4$\xcf\xf0\xe93j\x9f\x80\xf9\xb1\u02d6\x8c\xafk\x1d\x1cI2\xa4\x89\n\xdaUGK\x814\x00\x00\u07d4$\u06aa\xdd\xf7\xb0k\xbc\ua6c0Y\x00\x85\xa8\x85gh+N\x89\x11K \x15\u04bb\xd0\x00\x00\u07d4$\xdc\xc2K\xd9\xc7!\f\xea\u03f3\r\xa9\x8a\xe0JM{\x8a\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4$\xf7E\r\xdb\xf1\x8b\x02\x0f\xeb\x1a 2\xd9\xd5Kc>\xdf7\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4$\xfcs\xd2\a\x93\t\x8e\t\u076bW\x98Pb$\xfa\x1e\x18P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4$\xfd\x9al\x87L/\xab?\xf3n\x9a\xfb\xf8\xce\r2\xc7\u0792\x89Hz\x9a0E9D\x00\x00\u07d4%\n@\xce\xf3 #\x97\xf2@F\x95H\xbe\xb5bj\xf4\xf2<\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\u07d4%\niC\av\xf64w\x03\xf9R\x97\x83\x95Za\x97\xb6\x82\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4%\x0e\xb7\xc6o\x86\x9d\xdfI\u0685\xf39>\x98\f\x02\x9a\xa44\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x10j\xb6u]\xf8mkc\xa1\x87p;\f\xfe\xa0\u5520\x89\x01|@Z\xd4\x1d\xb4\x00\x00\xe0\x94%\x18_2Z\xcf-dP\x06\x98\xf6\\v\x9d\xdfh0\x16\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x1c\x12r,hy\"y\x92\xa3\x04\xeb5v\xcd\x18CN\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\x1eh8\xf7\xce\u0173\x83\xc1\xd9\x01F4\x12t\xda\xf8\xe5\x02\x89\a\xff\x1c\xcbua\xdf\x00\x00\u07d4%%\x9d\x97Z!\xd8:\xe3\x0e3\xf8\x00\xf5?7\u07e0\x198\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4%({\x81_\\\x828\ns\xb0\xb1?\xba\xf9\x82\xbe$\xc4\u04c9\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94%+eU\xaf\u0700\xf2\xd9m\x97-\x17\u06c4\xeaZ\xd5!\xac\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4%8S)6\x81<\x91\xe6S(O\x01|\x80\u00f8\xf8\xa3o\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94%>2\xb7N\xa4I\n\xb9&\x06\xfd\xa0\xaa%{\xf2=\u02cb\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94%?\x1et*,\uc1b0\u05f3\x06\xe5\xea\xcbl\xcb/\x85T\x8a\x04>^\xde\x1f\x87\x8c \x00\x00\u07d4%A1J\v@\x8e\x95\xa6\x94DIwq*Pq5\x91\xab\x89X\x9e\x1a]\xf4\u05f5\x00\x00\u07d4%L\x1e\xccc\f(w\u0780\x95\xf0\xa8\u06e1\xe8\xbf\x1fU\f\x89\\(=A\x03\x94\x10\x00\x00\u07d4%Z\xbc\x8d\b\xa0\x96\xa8\x8f=j\xb5_\xbcsR\xbd\u0739\u0389\x04t6\x821>\u0780\x00\u07d4%[\xdddt\u0302b\xf2j\"\u00cfE\x94\x0e\x1c\ue99b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%`\xb0\x9b\x89\xa4\xaehI\xedZ<\x99XBf1qDf\x89\\(=A\x03\x94\x10\x00\x00\u07d4%a\xa18\xdc\xf8;\xd8\x13\xe0\xe7\xf1\bd+\xe3\xde=o\x05\x8964\xf4\x84\x17@\x1a\x00\x00\u0794%a\xec\x0f7\x92\x18\xfe^\xd4\xe0(\xa3\xf7D\xaaAuLr\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u0794%b\x92\xa1\x91\xbd\xda4\xc4\xdakk\u0591G\xbfu\u2a6b\x88\xc2\xff.\r\xfb\x03\x80\x00\u07d4%i~\xf2\f\u032ap\xd3-7o\x82r\xd9\xc1\a\f=x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%o\xa1P\u0307\xb5\x05j\a\xd0\x04\xef\xc8E$s\x9eb\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4%r\x1c\x87\xb0\xdc!7|r\x00\xe5$\xb1J\"\xf0\xafi\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4%\x899\xbb\xf0\f\x9d\xe9\xafS8\xf5\xd7\x14\xab\xf6\xd0\xc1\xc6q\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94%\x90\x12hp\xe0\xbd\xe8\xa6c\xab\x04\nr\xa5W=\x8dA\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4%\x9e\xc4\xd2e\xf3\xabSk|p\xfa\x97\xac\xa1Bi,\x13\xfc\x89\x01\x1b\x1b[\xea\x89\xf8\x00\x00\xe0\x94%\xa5\x00\xee\xeczf*\x84\x15R\xb5\x16\x8bp{\r\xe2\x1e\x9e\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\xe0\x94%\xa5\xa4M8\xa2\xf4Lj\x9d\xb9\u037ck\x1e.\x97\xab\xb5\t\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4%\xa7L*\xc7]\u023a\xa8\xb3\x1a\x9c|\xb4\xb7\x82\x9b$V\u0689lk\x93[\x8b\xbd@\x00\x00\xe0\x94%\xad\xb8\xf9o9I,\x9b\xb4|^\u0708bNF\aV\x97\x8a\x05\xa9\x94\v\xc5hyP\x00\x00\u07d4%\xae\xe6\x8d\t\xaf\xb7\x1d\x88\x17\xf3\xf1\x84\xecV/x\x97\xb74\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xb0S;\x81\xd0*a{\x92)\xc7\xec]o/g.[Z\x8965\u026d\xc5\u07a0\x00\x00\u07d4%\xb7\x8c\x9f\xad\x85\xb43C\xf0\xbf\xcd\x0f\xac\x11\u0254\x9c\xa5\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4%\xbcI\xef(\x8c\xd1e\xe5%\xc6a\xa8\x12\u03c4\xfb\xec\x8f3\x89\x12Y!\xae\xbd\xa9\xd0\x00\x00\u07d4%\xbd\xfa>\xe2o8Ia{#\x00bX\x8a\x97\xe3\xca\xe7\x01\x8965\xe6\x19\xbb\x04\xd4\x00\x00\u07d4%\xc1\xa3~\xe5\xf0\x82e\xa1\xe1\r=\x90\xd5G)U\xf9x\x06\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4%\xc6\xe7O\xf1\xd9(\u07d8\x13z\xf4\u07c40\xdf$\xf0|\u05c9\x15$VU\xb1\x02X\x00\x00\xe0\x94%\xcf\xc4\xe2\\5\xc1;i\xf7\xe7}\xbf\xb0\x8b\xafXuk\x8d\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94%\xda\u0515\xa1\x1a\x86\xb9\xee\xec\xe1\xee\xec\x80^W\xf1W\xfa\xff\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94%\xe07\xf0\n\x18'\v\xa5\xec4 \"\x9d\xdb\n,\u33e2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4%\xe6a\xc99\x86:\xcc\x04No\x17\xb5i\x8c\xce7\x9e\xc3\u0309JD\x91\xbdm\xcd(\x00\x00\u07d4&\x04\x8f\xe8M\x9b\x01\nb\xe71b~I\xbc.\xb7?@\x8f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\x06\u00f3\xb4\xca\x1b\t\x14\x98`,\xb1\x97\x8b\xf3\xb9R!\xc0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4&\n#\x0eDe\a~\v\x14\xeeDB\xa4\x82\u0570\xc9\x14\xbf\x89Z\xf6\x06\xa0k[\x11\x80\x00\u07d4&\r\xf8\x94:\x8c\x9a]\xbayE2\u007f\xd7\xe0\x83|\x11\xad\a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4&\x14\xf4-]\xa8D7ux\xe6\xb4H\xdc$0[\xef+\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\x15\x10\x0e\xa7\xe2[\xba\x9b\xcat`X\xaf\xbb\xb4\xff\xbeBD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4&\x15u\xe9\xcfY\xc8\"o\xa7\xaa\xf9\x1d\xe8o\xb7\x0fZ\u00ee\x89\x10C\xa4CjR?\x00\x00\xe0\x94&\x1e\x0f\xa6LQ\x13te\xee\xcf[\x90\xf1\x97\xf7\x93\u007f\xdb\x05\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4&*\x8b\xfd}\x9d\xc5\xdd:\u05c1a\xb6\xbbV\b$76U\x89?j\x83\x84\a+v\x00\x00\xe0\x94&*\xedK\xc0\xf4\xa4\xb2\xc6\xfb5y>\x83ZI\x18\x9c\xdf\xec\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94&-\xc16L\xcfm\xf8\\C&\x8e\xe1\x82UM\xaei.)\x8a\x01\v /\xect\xce\xd8\x00\x00\u07d4&8\x140\x9d\xe4\xe65\xcfX^\r6Tw\xfc@\xe6l\xf7\x89\a\xea(2uw\b\x00\x00\u07d4&9\xee\xe9\x87<\xee\xc2o\u0314T\xb5H\xb9\xe7\xc5J\xa6\\\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94&>W\xda\xcb\xe0\x14\x9f\x82\xfee\xa2fH\x98\x86o\xf5\xb4c\x8a\b\v\xfb\xef\xcb_\v\xc0\x00\x00\u07d4>\x19\xc0m_\x14z\xa5\x97$\x8e\xb4l\xf7\xbe\xfad\xa5\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4&L\xc8\bj\x87\x10\xf9\x1b!r\t\x05\x91,\u05d6J\xe8h\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94&S\x83\u058bR\xd04\x16\x1b\xfa\xb0\x1a\xe1\xb0G\x94/\xbc2\x8a\x04rq\xde\xe2\rt\\\x00\x00\u07d4&Y\xfa\xcb\x1e\x83CeS\xb5\xb4)\x89\xad\xb8\a_\x99S\xed\x89\x01\x97evw\x1a^\x00\x00\xe0\x94&o-\xa7\xf0\b^\xf3\xf3\xfa\t\xba\xee#+\x93\xc7D\xdb.\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4&qH\xfdr\xc5Ob\nY/\xb9'\x991\x9c\xc4S+\\\x89\x169\u46fa\x16(\x00\x00\xe0\x94&xJ\u0791\u0228:\x8e9e\x8c\x8d\x82wA<\u0319T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4&z~n\x82\xe1\xb9\x1dQ\xde\u0776D\xf0\xe9m\xbb\x1f\u007f~\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\x80q=@\x80\x8e*P\xed\x011P\xa2\xa6\x94\xb9j\u007f\x1d\x89a\t=|,m8\x00\x00\u07d4&\x97\xb39\x81;\f-\x96K$q\xeb\x1c`oN\u02d6\x16\x89>\x8e\xf7\x95\u0610\xc8\x00\x00\u07d4&\xa6\x8e\xab\x90Z\x8b=\xce\x00\xe3\x170\x82%\u06b1\xb9\xf6\xb8\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4&\xb1\x1d\x06e\x88\xcet\xa5r\xa8Zc(s\x92\x12\xaa\x8b@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xba\xbfB\xb2g\xfd\xcf8a\xfd\xd4#j^GHH\xb3X\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xc0\x05Kp\r:|-\xcb\xe2uh\x9dOL\xad\x16\xa35\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xc2\xff\xc3\x0e\xfd\xc5'>v\x18:\x16\xc2i\x8dnS\x12\x86\x89*\x11)\u0413g \x00\x00\u07d4&\u025f\x88I\u0240+\x83\xc8a!\u007f\xd0z\x9e\x84\u0377\x9d\x89\x10CV\x1a\x88)0\x00\x00\u07d4&\xcf\xff\xd0R\x15+\xb3\xf9W\xb4x\xd5\xf9\x8b#:|+\x92\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4&\u0521h\x91\xf5)\"x\x92\x17\xfc\u0606\xf7\xfc\xe2\x96\xd4\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4&\xd4\xec\x17\xd5\u03b2\u0214\xbd\u015d\nji]\xad+C\u0309\x9f\x1fxv\x1d4\x1a\x00\x00\u07d4&\xe8\x01\xb6,\x82q\x91\xddh\xd3\x1a\x01\x19\x90\x94\u007f\xd0\xeb\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4&\xe9\xe2\xadr\x97\x02bd\x17\xef%\xde\r\xc8\x00\xf7\xa7y\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4&\xf9\xf7\xce\xfd~9K\x9d9$A+\xf2\u0083\x1f\xaf\x1f\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94&\xfe\x17L\xbfRfP\xe0\xcd\x00\x9b\xd6\x12e\x02\u038ehM\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\xe0\x94&\xff\nQ\xe7\xce\u0384\x00'ix\xdb\xd6#n\xf1b\xc0\xe6\x8a\x15.\x18V'T\nP\x00\x00\u07d4'\x10\x1a\x0fV\u04da\x88\u0168O\x9b2L\xdd\xe3>\\\xb6\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x14L\xa9\xa7w\x1a\x83j\xd5\x0f\x80?d\xd8i\xb2\xae+ \x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4'\x14i\x13V:\xa7E\xe2X\x840\xd94\x8e\x86\xea|5\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4'\x1d=H\x1c\xb8\x8evq\xad!iI\xb66^\x060=\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4' \xf9\xcaBn\xf2\xf2\xcb\xd2\xfe\xcd9\x92\fO\x1a\x89\xe1m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'*\x13\x1aZejz:\xca5\u023d \"\"\xa7Y\"X\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4'D\xffgFA!\xe3Z\xfc)\"\x17qd\xfa/\xcb\x02g\x89\x05k\xc7^-c\x10\x00\x00\u07d4'J=w\x1a=p\x97\x96\xfb\xc4\xd5\xf4\x8f\xce/\xe3\x8cy\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4'Mi\x17\x0f\xe7\x14\x14\x01\x88+\x88j\xc4a\x8cj\xe4\x0e\u06c93\xc5I\x901r\f\x00\x00\u07d4'R\x1d\xeb;n\xf1An\xa4\u01c1\xa2\xe5\u05f3n\xe8\x1ca\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'Xu\xffO\xbb\f\xf3\xa40!1'H\u007fv\b\xd0L\xba\x89\x1b\x1c\x01\x0evmX\x00\x00\u07d4'j\x00n0(\xec\xd4L\xdbb\xba\nw\u0394\xeb\xd9\xf1\x0f\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4'k\x05!\xb0\xe6\x8b'}\xf0\xbb2\xf3\xfdH2cP\xbf\xb2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4'o\xd7\xd2O\x8f\x88?Zz()[\xf1qQ\u01e8K\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'p\xf1N\xfb\x16]\u07bay\xc1\v\xb0\xaf1\xc3\x1eY3L\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4'vw\xab\xa1\xe5,;S\xbf\xa2\a\x1dN\x85\x9a\n\xf7\xe8\xe1\x8965\u026d\xc5\u07a0\x00\x00\u07d4'\x82Ff\xd2x\xd7\x04#\xf0=\xfe\x1d\u01e3\xf0/C\u2d4966\xc2^f\xec\xe7\x00\x00\u07d4'\x83\f_`#\xaf\xaa\xf7\x97Egl J\x0f\xac\u0360\xba\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94'\x84\x90?\x1d|\x1b\\\xd9\x01\xf8\x87]\x14\xa7\x9b<\xbe*V\x8a\x04\xbd\xa7\xe9\xd7J\xd5P\x00\x00\u07d4'\x8c\v\xdec\x0e\u00d3\xb1\xe7&\u007f\xc9\xd7\xd9p\x19\xe4\x14[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4'\x98q\x10\"\x1a\x88\b&\xad\xb2\xe7\xab^\xcax\xc6\xe3\x1a\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94'\xac\a;\xe7\x9c\xe6W\xa9:\xa6\x93\xeeC\xbf\x0f\xa4\x1f\xef\x04\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4'\xb1iN\xaf\xa1e\xeb\xd7\xcc{\u025et\x81J\x95\x14\x19\u0709+^:\xf1k\x18\x80\x00\x00\u07d4'\xb6(\x16\xe1\xe3\xb8\u045by\xd1Q=]\xfa\x85[\f:*\x89\x05j\xf5\xc1\xfdiP\x80\x00\u07d4'\xbf\x94<\x163\xfe2\xf8\xbc\xcc\xdbc\x02\xb4\a\xa5rND\x892\xf8Lm\xf4\b\xc0\x80\x00\u07d4'\xbf\x9fD\xba}\x05\xc35@\u00e5;\xb0,\xbb\xff\xe7\xc3\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4'\xc2\xd7\xcaPM\xaa=\x90f\xdc\t\x13}\xc4/:\xaa\xb4R\x89 \x86\xac5\x10R`\x00\x00\u07d4'\xd1X\xac=>\x11\t\xabnW\x0e\x90\xe8]8\x92\xcdv\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4'\xe69\x89\xca\x1e\x90;\xc6 \xcf\x1b\x9c?g\xb9\xe2\xaee\x81\x89Hz\x9a0E9D\x00\x00\xe0\x94'\xf0<\xf1\xab\xc5\xe1\xb5\x1d\xbcDK(\x9eT,\x9d\u07f0\xe6\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4'\xfc\x85\xa4\x9c\xff\x90\xdb\xcf\xda\u071d\xdd@\u05b9\xa2!\nl\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\x05A^\x1d\u007f\xde\xc6\xde\u07f8\x9eR\x1d\x10Y-t<\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4(\a>\xfc\x17\xd0\\\xab1\x95\xc2\xdb3+a\x98Gw\xa6\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x12P\xa2\x91!'\nN\xe5\u05cd$\xfe\xaf\xe8,p\xba:\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x13\xd2c\xfc_\xf2G\x9e\x97\x05\x95\u05b6\xb5`\xf8\xd6\xd6\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4(.\x80\xa5T\x87ZVy\x9f\xa0\xa9\u007fU\x10\u7557LN\x8965\u026d\xc5\u07a0\x00\x00\u07d4(3\x96\xce<\xac9\x8b\xcb\xe7\"\u007f2>x\xff\x96\u0407g\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4(4\x9f~\xf9t\xeaU\xfe6\xa1X;4\xce\xc3\xc4Pe\xf0\x89\f\xb63\u051eeY\x00\x00\u07d4(6\x120F\xb2\x84\xe5\xef\x10+\xfd\"\xb1v^P\x81\x16\xad\x89\x16S\xfb\xb5\xc4'\xe4\x00\x00\u07d4(<#\x14(<\x92\u0530d\xf0\xae\xf9\xbbRF\xa7\x00\u007f9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(>\x11 7I\xb1\xfaO2\xfe\xbbq\xe4\x9d\x13Y\x198*\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(>bR\xb4\xef\xcfFT9\x1a\xcbu\xf9\x03\u015bx\xc5\xfb\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94(Q\x0en\xff\x1f\xc8)\xb6WoC(\xbc98\xecze\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4(X\xac\xac\xaf!\xea\x81\u02b7Y\x8f\xdb\xd8kE.\x9e\x8e\x15\x89$\x1a\x9bOaz(\x00\x00\u07d4(Z\xe5\x1b\x95\x00\u014dT\x13e\xd9ui\xf1K\xb2\xa3p\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(f\xb8\x1d\xec\xb0.\xe7\n\xe2P\xce\xe5\xcd\xc7{Y\u05f6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(i\x06\xb6\xbdIr\xe3\xc7\x16U\xe0K\xaf6&\f|\xb1S\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4(k\x18ma\xea\x1f\u05cd\x990\xfe\x12\xb0e7\xb0\\=Q\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94(t\xf3\xe2\x98]_{@f'\xe1{\xaaw+\x01\xab\u031e\x8a\x01F\x05\x04\x10v_8\x00\x00\xe0\x94(|\xf9\u0410.\xf8\x19\xa7\xa5\xf1ID[\xf1w^\xe8\xc4|\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4(\x81\x8e\x18\xb6\x10\x00\x13!\xb3\x1d\xf6\xfe}(\x15\u036d\xc9\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4(\x86\x83$3~\x11\xba\x10l\xb4\x81\u0696/:\x84S\x80\x8d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94(\x90K\xb7\xc40)C\xb7\t\xb1Myp\xe4+\x83$\u184a\x02\x1f\x97\x84j\a-~\x00\x00\u07d4(\x95\xe8\t\x99\xd4\x06\xadY.+&'7\xd3_}\xb4\xb6\x99\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4(\x96r\x80!N!\x8a\x12\f]\xda7\x04\x1b\x11\x1e\xa3mt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4(\xa3\xda\t\xa8\x19H\x19\xae\x19\x9f.m\x9d\x13\x04\x81~(\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4(\xab\x16_\xfbi\xed\xa0\xc5I\xae8\xe9\x82o_\u007f\x92\xf8S\x89FM\xf6\xd7\xc8DY\x00\x00\u07d4(\xb7u\x85\xcb=U\xa1\x99\xab)\x1d:\x18\u018f\u8684\x8a\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94(\xd4\xeb\xf4\x1e=<E\x1e\x94;\xdd~\x1f\x17_\xae\x93*=\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94(\xd7\xe5\x86o\x1d\x85\xfd\x1c\xeb2\xbf\xbe\x1d\xfc6\xdbCEf\x8a\x01\x86B1\xc6\x105\x1c\x00\x00\u0794(\xd8\xc3_\xb7\xee\xa6\"X!5\xe3\xadG\xa2'\u0266c\xbd\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4(\xe4\xaf0\u0353\xf6\x86\xa1\"\xad{\xb1\x9f\x8a\x87\x85\xee\xe3B\x89q\xe5;pl\u01f4\x00\x00\u07d4(\xea\xeax\xcdM\x95\xfa\xec\xfbh\x83n\xaf\xe85 \U000fbdc9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94(\xef\xaecVP\x9e\u07ec\xe8\x9f\xc6\x1a\u007f\xdc\xdb9\xee\xa8\xe5\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\xe0\x94(\xfa%\x80\xf9\xeb\xe4 \xf3\xe5\xee\xfd\xd3qc\x8e;z\xf4\x99\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4)\x01\xf8\a\u007f4\x19\v\xb4z\x8e\"\u007f\xa2\x9b0\xce\x11;1\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\x05\xb1\x92\xe8<\xe6Y\xaa5[\x9d\f N>\x95\xf9\xbb\x9a\x89u#\\\x1d\x009>\x80\x00\u07d4)\nV\xd4\x1fn\x9e\xfb\xdc\xea\x03B\u0dd2\x9a\x8c\xdf\xcb\x05\x89\x12\xa5\xf5\x81h\xee`\x00\x00\u07d4)\x15bK\xcbg\x917\xb8\xda\xe9\xabW\xd1\x1bI\x05\xea\xeeK\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4)\x1e\xfe\x00\x81\xdc\xe8\xc1G\x99\xf7\xb2\xa46\x19\xc0\u00f3\xfc\x1f\x89A\rXj \xa4\xc0\x00\x00\u07d4)\x1f\x92\x9c\xa5\x9bT\xf8D>=Mu\xd9]\xee$<\xefx\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x94))\x8c\xcb\xdf\xf6\x89\xf8\u007f\xe4\x1a\xa6\xe9\x8f\u07f5=\xea\xf3z\x8a\x041\\2\xd7\x1a\x9e`\x00\x00\u07d4)/\"\x8b\n\x94t\x8c\x8e\xeca-$o\x98\x93c\xe0\x8f\b\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4)3\x84\xc4+o\x8f)\x05\xceR\xb7 \\\"t7la+\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4)4\xc0\xdf{\xbc\x17+l\x18k\vrTz\u038b\xf7TT\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4)<#\x06\xdf6\x04\xaeO\xda\r z\xbasog\xde\a\x92\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)I\xfd\x1d\xef\\v\xa2\x86\xb3\x87$$\x80\x9a\a\xdb9f\xf3\x8a\x01\x1b\xd9\x06\u06a0\xc9C\x80\x00\u07d4)OIK?.\x14</\xfc\x978\xcb\xfd\x95\x01\x85\v\x87N\x89yn>\xa3\xf8\xab\x00\x00\x00\u07d4)U\xc3W\xfd\x8fu\xd5\x15\x9a=\xfai\u0178z5\x9d\ua309lk\x93[\x8b\xbd@\x00\x00\u07d4)a\xfb9\x1ca\x95|\xb5\xc9\xe4\a\u0762\x938\u04f9,\x80\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4)h\x1d\x99\x12\xdd\xd0~\xaa\xbb\x88\xd0]\x90\xf7f\xe8bA}\x8965\u026d\xc5\u07a0\x00\x00\u07d4)kq\xc0\x01X\x19\xc2B\xa7\x86\x1eo\xf7\xed\xed\x8a_q\xe3\x89lh\xcc\u041b\x02,\x00\x00\u07d4)mf\xb5!W\x1aNA\x03\xa7\xf5b\xc5\x11\xe6\xaas-\x81\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4)o\x00\xde\x1d\u00fb\x01\xd4z\x8c\xcd\x1e]\x1d\u0661\xebw\x91\x8965\u026d\xc5\u07a0\x00\x00\u07d4)s\x85\xe8\x864FV\x85\xc21\xa3\x14\xa0\xd5\xdc\xd1F\xaf\x01\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4)v=\xd6\u069a|\x16\x11s\x88\x83!\ub9b6<\x8f\xb8E\x89\x11\xc7\xea\x16.x \x00\x00\u07d4)yt\x11t\xa8\xc1\xea\v\u007f\x9e\xdfe\x81w\x85\x94\x17\xf5\x12\x89\x19\x01\x96l\x84\x96\x83\x80\x00\u07d4)z\x88\x92\x1b_\xca\x10\u5edd\xed`\x02T7\xae\"\x16\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94)}]\xbe\"//\xb5%1\xac\xbd\v\x01=\xc4F\xacsh\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\x82N\x94\xccCH\xbc\x962y\xdc\xdfG9\x17\x152L\u04c9i*\xe8\x89p\x81\xd0\x00\x00\u07d4)\x82\xd7j\x15\xf8G\xddA\xf1\x92*\xf3h\xfeg\x8d\x0eh\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\x88\x87\xba\xb5|[\xa4\xf0aR)\xd7R_\xa1\x13\xb7\ua249\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94)\x8e\xc7kD\r\x88\a\xb3\xf7\x8b_\x90\x97\x9b\xeeB\xedC\u06ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4)\x93h`\x90B\xa8X\xd1\xec\xdf\x1f\xc0\xad\xa5\xea\xce\xca)\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4)\x9e\v\xcaU\xe0i\u0785\x04\xe8\x9a\xcan\xca!\u04ca\x9a]\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4)\xac+E\x84T\xa3l~\x96\xc7:\x86g\"*\x12$,q\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xad\u03c3\xb6\xb2\n\u01a44\xab\xb1\x99<\xbd\x05\xc6\x0e\xa2\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94)\xae\xf4\x8d\xe8\xc9\xfb\xadK\x9eL\xa9pyzU3\xebr-\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4)\xb3\xf5a\xeezn%\x94\x1e\x98\xa52[x\xad\u01d7\x85\xf3\x89\x05k\xc7^-c\x10\x00\x00\u07d4)\xbd\xc4\xf2\x8d\xe0\x18\x0fC<&\x94\xebt\xf5PL\xe9C7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\u0300M\x92+\xe9\x1fY\t\xf3H\xb0\xaa\xa5\xd2\x1b`x0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94)\xda>5\xb2;\xb1\xf7/\x8e\"X\xcf\u007fU3Y\xd2K\xac\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94)\xe6y\x90\xe1\xb6\xd5.\x10U\xff\xe0I\xc51\x95\xa8\x15B\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4)\uab82v\x17b\xf4\xd2\xdbS\xa9\u018b\x0fk\vmNf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4)\xeb~\xef\xda\xe9\xfe\xb4I\xc6?\xf5\xf2y\xd6u\x10\xeb\x14\"\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4)\xf0\xed\xc6\x038\xe7\x11 \x85\xa1\xd1\x14\u068cB\u038fU\u0589\xa0Z\u007f\x0f\xd8%x\x00\x00\u07d4)\xf8\xfb\xa4\xc3\ar\xb0W\xed\xbb\xe6*\xe7B\f9\x05r\xe1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94)\xf9(l\x0es\x8d\x17!\xa6\x91\u01b9Z\xb3\u0667\x97\xed\xe8\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4*\b^%\xb6Hb\xf5\xe6\x8dv\x8e+\x0fz\x85)\x85\x8e\xee\x89k\x88:\xcdWf\xcd\x00\x00\u07d4**\xb6\xb7Lz\xf1\xd9Gk\xb5\xbc\xb4RG\x97\xbe\xdc5R\x8965\u026d\xc5\u07a0\x00\x00\u07d4*9\x19\nO\u0783\u07f3\xdd\xcbL_\xbb\x83\xaclIu\\\x8965\u026d\xc5\u07a0\x00\x00\u07d4*@\r\xff\x85\x94\xder(\xb4\xfd\x15\xc3#\"\xb7[\xb8}\xa8\x89\x051\xa1\u007f`z-\x00\x00\xe0\x94*D\xa7!\x8f\xe4Me\xa1\xb4\xb7\xa7\u0671\xc2\xc5,\x8c>4\x8a\r-\x06\xc3\x05\xa1\xebW\x80\x00\u07d4*F\xd3Swqv\xff\x8e\x83\xff\xa8\x00\x1fOp\xf9s:\xa5\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4*Y_\x16\xee\xe4\xcb\f\x17\u0662\xd99\xb3\xc1\x0flgrC\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4*Y\xe4~\xa5\xd8\xf0\xe7\xc0(\xa3\xe8\xe0\x93\xa4\x9c\x1bP\xb9\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*[\xa9\xe3L\u054d\xa5L\x9a'\x12f:;\xe2t\xc8\xe4{\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94*^:@\xd2\xcd\x03%vm\xe7:=g\x18\x96\xb3b\xc7;\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94*cY\x0e\xfe\x99\x86\xc3\xfe\xe0\x9b\n\n3\x8b\x15\xbe\xd9\x1f!\x8a\x01^\x1cN\x05\xee&\xd0\x00\x00\u07d4*gf\n\x13h\xef\xcdbn\xf3k+\x1b`\x19\x80\x94\x1c\x05\x89\a?u\u0460\x85\xba\x00\x00\u07d4*t+\x89\x10\x94\x1e\t2\x83\n\x1d\x96\x92\xcf\u0484\x94\xcf@\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4*tl\xd4@'\xaf>\xbd7\xc3x\xc8^\xf7\xf7T\xab_(\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4*\x81\xd2|\xb6\xd4w\x0f\xf4\xf3\u0123\xba\x18\xe5\xe5\u007f\aQ|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*\x91\xa9\xfe\xd4\x1b}\x0e\\\xd2\xd81X\xd3\xe8\xa4\x1a\x9a-q\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94*\x9cW\xfe{k\x13\x8a\x92\rgo<v\xb6\u00a0\xee\xf6\x99\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4*\x9c\x96\xc1\x91Q\xff\xcb\xe2\x9aF\x16\xd0\xc5+93\xb4e\x9f\x89\x03\xc17\x9b\x87e\xe1\x80\x00\u07d4*\xa1\x92w|\xa5\xb9x\xb6\xb2\xc2\xff\x80\n\xc1\x86\x0fu?G\x89\x12)\x0f\x15\x18\v\xdc\x00\x00\xe0\x94*\xaa5'Mt%Fg\vt&&E!\x03*\xf4\xf4\u00ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4*\xae\xa1\xf1\x04o0\xf1\t\xfa\xec\x1cc\xef\\u\x94\xeb\b\u0689\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4*\xb9~\x8dY\xee\xe6H\xabl\xaf\x86\x96\xf8\x997\x148d\u0589\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94*\xbc\u1009@\xcdN\xf5\xb5\xe0R\x85\xf8-\xf7\xa9\xab^\x03\x8a\x02\x13BR\r_\xec \x00\x00\xe0\x94*\xbd\xf1\xa67\xeflB\xa7\xe2\xfe!ws\xd6w\xe8\x04\xeb\u074a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4*\xc1\xf8\u05ffr\x1f<\xfet\xd2\x0f\ua6c7\xa2\x8a\xaa\x98,\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\xe0\x94*\u031c\x1a2$\vM[/wz.\xa0R\xb4/\xc1'\x1c\x8a\b\xd8\a\xee\x14\xd86\x10\x00\x00\u07d4*\xd6\xc9\xd1\f&\x18\x19\xa1\xa0\xca,H\xd8\u01f2\xa7\x17(\u07c965\u026d\xc5\u07a0\x00\x00\xe0\x94*\xe58f\xfc-\x14\xd5r\xabs\xb4\xa0e\xa1\x18\x82g\xf5'\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4*\xe7:y\xae\xa0'\x853\xac\xcf!\a\t\"\xb1a?\x8f2\x89\xa7\xe9K\xbe\xaep\x1a\x80\x00\u07d4*\xe8-\xab\x92\xa6c\x89\ue86b\xb9\x01\xd1\xd5\u007fZ|\xca\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4*\uc01d\xf92[\x9fH9\x96\xe9\x9fs1\t\u007f\b\xaa\x0e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94*\xed,\xe51\xc0V\xb0\t~\xfc<m\xe1\fGb\x00N\u064a\x025iS\xab}\xdc8\x00\x00\u07d4*\xfb\x05\x8c=1\x03+5;\xf2O\t\xae \xd5M\xe5}\xbe\x89;\xa1\x91\v\xf3A\xb0\x00\x00\xe0\x94+\x03bc6\x14\xbf\xcbX5iC\x8e\xccN\xa5{\x1d3~\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4+\x10\x1e\x82,\xd9b\x96*\x06\x80\n,\b\u04f1]\x82\xb75\x89\b=lz\xabc`\x00\x00\u07d4+\x12\x9c&\xb7]\xde\x12\u007f\x83 \xbd\x0fcA\f\x92\xa9\xf8v\x89wC\"\x17\xe6\x83`\x00\x00\xe0\x94+$\x1f\x03s7\xebJ\xcca\x84\x9b\xd2r\xac\x13?|\xdfK\x8aP\vk\u0296*\xb8@\x00\x00\u07d4+:h\xdbk\f\xae\x8a|zGk\xdf\u03fdb\x05\xe1\x06\x87\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4+<\xf9s\x11\xff0\xf4`\x94Z\x9d\x80\x99\xf4\xa8\x8e&\xd4V\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+I\xfb\xa2\x9806\x0f\u0376\xda#\xbb\xfe\xa5\xc0\xbb\xacR\x81\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794+OE\a\xbbk\x98\x17\x94,\xe43x\x1bp\x8f\xbc\xd1f\xfd\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94+P\x16\xe2Es\x87\x95ebXq\x15\xaa\x87Y\xd8i_\u07ca*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94+\\`\xe8E5\xee\xb4\u0540\xde\x12z\x12\xeb&w\u0333\x92\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4+\\\ud647\xc0v_\x90\x0eI\u03dd\xa2\xd9\xf9\xc1\x13\x88U\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94+_K?\x1e\x11pz\"z\xa5\u67e4\x9d\xde\xd3?\xb3!\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4+h0k\xa7\xf8\u06afs\xf4\xc6D\xef}'C\xc0\xf2hV\x89.\xe1\x82\xca\x17\xdd\xd0\x00\x00\xe0\x94+n\u049a\x95u<:\xd9H4\x8e>{\x1a%\x10\x80\xff\xb9\x8a4\xf0\x86\xf3\xb3;h@\x00\x00\u07d4+p\x1d\x16\xc0\xd3\xcc\x1eL\xd8TE\xe6\xad\x02\ue92c\x01-\x89 \x86\xac5\x10R`\x00\x00\xe0\x94+q|\xd42\xa3#\xa4e\x909\x84\x8d;\x87\xde&\xfc\x95F\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4+t\xc3s\xd0K\xfb\x0f\xd6\n\x18\xa0\x1a\x88\xfb\xe8Gp\u5309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4+w\xa4\u060c\rV\xa3\xdb\xe3\xba\xe0J\x05\xf4\xfc\u0477W\xe1\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94+\x84\x88\xbd-<\x19z=&\x15\x18\x15\xb5\xa7\x98\xd2qh\u070a\x01j\x1f\x9f_\xd7\xd9`\x00\x00\u07d4+\x8a\r\xee\\\xb0\xe1\xe9~\x15\xcf\xcan\x19\xad!\xf9\x95\ufb49\x1bUC\x8d\x9a$\x9b\x00\x00\xe0\x94+\x8f\xe4\x16n#\xd1\x19c\xc0\x93+\x8a\u078e\x01E\xea\ap\x8a\t(\x96R\x9b\xad\u0708\x00\x00\xe0\x94+\x99\xb4.OBa\x9e\xe3k\xaa~J\xf2\xd6^\xac\xfc\xba5\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4+\xab\x0f\xbe(\u0544 \xb5 6w\n\x12\xf9\x95*\xeai\x11\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4+\xad\xe9\x1d\x15E\x17b\x0f\u05349\xac\x97\x15zA\x02\xa9\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4+\xaf\x8dn\"\x11t\x12H \xeeI+\x94Y\xecO\xad\xaf\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xaf\xbf\x9e\x9e\xd2\xc2\x19\xf7\xf2y\x13t\xe7\xd0\\\xb0gw\xe7\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94+\xb3f\xb9\xed\xcb\r\xa6\x80\xf0\xe1\v;n(t\x81\x90\xd6\u00ca\x01:b\u05f5v@d\x00\x00\xe0\x94+\xb6\xf5x\xad\xfb\u7ca1\x16\xb3UO\xac\xf9\x96\x98\x13\xc3\x19\x8a\x01\x91'\xa19\x1e\xa2\xa0\x00\x00\u07d4+\xbeb\xea\xc8\f\xa7\xf4\xd6\xfd\xee~}\x8e(\xb6:\xcfw\x0e\x89\x81\xe3-\xf9r\xab\xf0\x00\x00\u07d4+\xbeg*\x18WP\x8fc\x0f*^\xdbV=\x9e\x9d\xe9(\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4+\xc4)\xd6\x18\xa6jL\xf8-\xbb-\x82N\x93V\xef\xfa\x12j\x89lj\xccg\u05f1\xd4\x00\x00\u07d4+\xd2R\xe0\xd72\xff\x1d|x\xf0\xa0.l\xb2T#\xcf\x1b\x1a\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4+\xdd\x03\xbe\xbb\xee';l\xa1\x05\x9b4\x99\x9a[\xbda\xbby\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\x04\x11\\>R\x96\x1b\r\xc0\xb0\xbf1\xfb\xa4ToYf\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94,\x06\u0752+aQJ\xaf\xed\xd8D\x88\xc0\u008em\xcf\x0e\x99\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94,\f\xc3\xf9QH,\u0222\x92X\x15hN\xb9\xf9N\x06\x02\x00\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4,\x0e\xe14\u0633aE\xb4{\xee\u7bcd'8\xdb\xda\b\xe8\x89\n\xe5os\x0em\x84\x00\x00\u07d4,\x0f[\x9d\xf46%y\x8e~\x03\xc1\xa5\xfdjm\t\x1a\xf8+\x89\x01\xb0\xfc\xaa\xb2\x000\x00\x00\u07d4,\x12\x8c\x95\xd9W!Q\x01\xf0C\u074f\u0142EmA\x01m\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4,\x18\x00\xf3_\xa0->\xb6\xff[%(_^J\xdd\x13\xb3\x8d\x891\"\u04ed\xaf\xde\x10\x00\x00\u07d4,\x1c\x19\x11N=m\xe2xQHK\x8d'\x15\xe5\x0f\x8a\x10e\x89\x05k\xc7^-c\x10\x00\x00\u07d4,\x1c\xc6\xe1\x8c\x15$\x88\xba\x11\xc2\xcc\x1b\xce\xfa-\xf3\x06\xab\u0449Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94,\x1d\xf8\xa7oH\xf6\xb5K\u03dc\xafV\xf0\xee\x1c\xf5z\xb3=\x8a\x02$\u007fu\x00\x89\xdaX\x00\x00\u07d4,!G\x94z\xe3?\xb0\x98\xb4\x89\xa5\xc1k\xff\xf9\xab\xcdN*\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,#OP\\\xa8\xdc\xc7}\x9b~\x01\xd2W\xc3\x18\xcc\x199m\x89\x05k\xc7^-c\x10\x00\x00\u07d4,$(\xe4\xa6it\xed\xc8\"\xd5\xdb\xfb$\x1b'(\aQX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,-\x15\xff9V\x1c\x1br\xed\xa1\xcc\x02\u007f\xfe\xf27C\xa1D\x89\u0500\xed\x9e\xf3+@\x00\x00\u07d4,-\xb2\x8c3\t7^\xea<mr\xcdm\x0e\xec\x14Z\xfc\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,BN\xe4\u007fX<\xdc\xe0z\xe3\x18\xb6\xfa\xd4b8\x1dM+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94,KG\x03\a\xa0Y\x85@U\xd9\x1e\xc3yM\x80\xb5=\x0fJ\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4,R\u0244\x10.\xe0\xcd>1\x82\x1b\x84\xd4\b\x93\x0e\xfa\x1a\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4,Z-\n\xbd\xa0;\xbe!W\x81\xb4\xff)l\x8ca\xbd\xba\xf6\x89\x01\xa8\xe5oH\xc0\"\x80\x00\u07d4,[}{\x19Z7\x1b\xf9\xab\u0774/\xe0O/\x1d\x9a\x99\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4,]\xf8ffj\x19K&\u03bb@~J\x1f\xd7> \x8d^\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94,`?\xf0\xfe\x93alCW>\xf2y\xbf\xea@\x88\x8dj\xe7\x8a\x01\x00\xf4\xb6\xd6gW\x90\x00\x00\xe0\x94,hF\xa1\xaa\x99\x9a\"F\xa2\x87\x05`\x00\xbaM\u02e8\xe6=\x8a\x02\x1f/o\x0f\xc3\xc6\x10\x00\x00\u0794,j\xfc\xd4\x03|\x1e\xd1O\xa7O\xf6u\x8e\tE\xa1\x85\xa8\xe8\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4,ki\x9d\x9e\xad4\x9f\x06\u007fEq\x1a\aJd\x1d\xb6\xa8\x97\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,o\\\x12L\u01c9\xf8\xbb9\x8e?\x88\x97Q\xbcK`-\x9e\x89\x01Y\xf2\v\xed\x00\xf0\x00\x00\u07d4,\x83\xae\xb0/\xcf\x06}e\xa4p\x82\xfd\x97x3\xab\x1c\uc449\b'8#%\x8a\xc0\x00\x00\xe0\x94,\x89\xf5\xfd\xca=\x15T\t\xb68\xb9\x8at.U\xebFR\xb7\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4,\x96HI\xb1\xf6\x9c\xc7\u03a4D%8\xed\x87\xfd\xf1l\xfc\x8f\x89lk\x93[\x8b\xbd@\x00\x00\u0794,\x9f\xa7,\x95\xf3}\b\xe9\xa3`\t\u7930\u007f)\xba\xd4\x1a\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4,\xafk\xf4\xec}Z\x19\xc5\xe0\x89z^\xeb\x01\x1d\xce\xceB\x10\x89\a\x93H5\xa01\x16\x00\x00\u07d4,\xb4\xc3\xc1k\xb1\xc5^|kz\x19\xb1'\xa1\xac\x93\x90\xcc\t\x89\xb8'\x94\xa9$O\f\x80\x00\xe0\x94,\xb5IZPS6\xc2FT\x10\xd1\xca\xe0\x95\xb8\xe1\xba\\\u074a\x04<3\xc1\x93ud\x80\x00\x00\u07d4,\xb6\x15\a:@\xdc\u06d9\xfa\xa8HW.\x98{;\x05n\xfb\x89+X\xad\u06c9\xa2X\x00\x00\u07d4,\xbam]\r\xc2\x04\xea\x8a%\xad\xa2\xe2oVu\xbd_/\u0709H#\xef}\u06da\xf3\x80\x00\u07d4,\xbb\fs\u07d1\xb9\x17@\xb6i;wJ}\x05\x17~\x8eX\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4,\xcbfIM\n\xf6\x89\xab\xf9H=6]x$D\xe7\u07ad\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\xcc\x1f\x1c\xb5\xf4\xa8\x00.\x18k \x88]\x9d\xbc\x03\f\b\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u03c0\xe2\x18\x98\x12^\xb4\xe8\a\u0342\xe0\x9b\x9d(Y/n\x89lk\x93[\x8b\xbd@\x00\x00\u07d4,\u0456\x94\u0452j\x0f\xa9\x18\x9e\u07ba\xfcg\x1c\xf1\xb2\u02a5\x8965\u026d\xc5\u07a0\x00\x00\u07d4,\u04d34\xac~\xacyrW\xab\xe3sa\x95\xf5\xb4\xb5\xce\x0f\x89\x05kGx^7&\x00\x00\u07d4,\u05de\xb5 '\xb1,\x18\x82\x8e>\xaa\xb2\x96\x9b\xfc\u0487\xe9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4,\xd8xfV\x8d\xd8\x1a\xd4}\x9d:\u0404nZePss\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4,\xdb9De\x06\x16\xe4|\xb1\x82\xe0`2/\xa1Hyx\u0389b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4,\xe1\x1a\x92\xfa\xd0$\xff+>\x87\xe3\xb5B\xe6\xc6\r\xcb\u0656\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4-\x03&\xb2?\x04\t\xc0\xc0\xe9#hc\xa13\aZ\x94\xba\x18\x89\vg\x9b\xe7[\xe6\xae\x00\x00\u07d4-\r\xecQ\xa6\xe8s0\xa6\xa8\xfa*\x0fe\u060dJ\xbc\xdfs\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4-#vkok\x05s}\xad\x80\xa4\x19\xc4\x0e\xdaMw\x10>\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4-+\x03#Y\xb3c\x96O\xc1\x1aQ\x82c\xbf\xd0T1\xe8g\x89\b\x1c\x1d\xf7b\x9ep\x00\x00\u07d4-4\x80\xbf\be\aJr\xc7u\x9e\xe5\x13{Mp\xc5\x1c\xe9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-5\xa9\xdfbu\u007f\u007f\xfa\xd1\x04\x9a\xfb\x06\xcaJ\xfcFLQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-@U\x8b\x06\xf9\n9#\x14U\x92\x12;gt\xe4n1\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4-Bi\x12\xd0Y\xfa\xd9t\v.9\n.\xea\xc0To\xf0\x1b\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4-S-\xf4\xc69\x11\xd1\u0391\xf6\xd1\xfc\xbf\xf7\x96\x0fx\xa8\x85\x89Z\x85\x96\x8aXx\u0680\x00\u07d4-S\x91\xe98\xb3HX\u03d6[\x84\x051\xd5\xef\xdaA\v\t\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94-[B\xfcY\xeb\xda\r\xfdf\xae\x91K\u008c\x1b\nn\xf8:\x8a+\u0235\x9f\xdc\xd86c\x80\x00\u07d4-]s5\xac\xb06+G\u07e3\xa8\xa4\xd3\xf5\x94\x95D\u04c0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-a\xbf\xc5hs\x92<+\x00\t]\xc3\xea\xa0\xf5\x90\u062e\x0f\x8a\x04ef\xdf\xf8\xceU`\x00\x00\u07d4-e\x11\xfdz8\x00\xb2hT\xc7\xec9\xc0\u0735\xf4\xc4\xe8\xe8\x89\x15\xad\u077a/\x9ew\x00\x00\u07d4-}\\@\u076f\xc4P\xb0Jt\xa4\u06bc+\xb5\xd6e\x00.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\x89\xa8\x00jO\x13z \xdc+\xecF\xfe.\xb3\x12\xea\x96T\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4-\x8cR2\x9f8\u04a2\xfa\x9c\xba\xf5\u0143\xda\xf1I\v\xb1\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4-\x8e\x06\x18\x92\xa5\xdc\xce!\x96j\xe1\xbb\a\x88\xfd>\x8b\xa0Y\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4-\x8e[\xb8\xd3R\x16\x95\xc7~|\x83N\x02\x91\xbf\xac\xeet\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4-\x90\xb4\x15\xa3\x8e.\x19\xcd\xd0/\U000ed069z\xf7\xcb\xf6r\x89\x05\xf3\xc7\xf6A1\xe4\x00\x00\u07d4-\x9b\xado\x1e\xe0*p\xf1\xf1=\xef\\\u0332z\x9a'@1\x89a\t=|,m8\x00\x00\u07d4-\x9c_\xec\u04b4O\xbbj\x1e\xc72\xea\x05\x9fO\x1f\x9d+\\\x896\xca2f\x1d\x1a\xa7\x00\x00\xe0\x94-\xa6\x17iP\t\xccW\xd2j\u0510\xb3*]\xfb\xeb\x93N^\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4-\xa7k|9\xb4 \u323a,\x10 \xb0\x85k\x02pd\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4-\u01ddn\u007fU\xbc\xe2\xe2\xd0\xc0*\xd0|\uca3bR\x93T\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94-\xca\x0eD\x9a\xb6F\xdb\xdf\u04d3\xa9fb\x96\v\u02b5\xae\x1e\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4-\xd3%\xfd\xff\xb9{\x19\x99R\x84\xaf\xa5\xab\xdbWJ\x1d\xf1j\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4-\xd5x\xf7@}\xfb\xd5H\xd0^\x95\xcc\u00dcHT)bj\x89\u3bb5sr@\xa0\x00\x00\u07d4-\xd8\xee\xef\x87\x19J\xbc,\xe7X]\xa1\xe3[|\xeax\f\xb7\x8965\xc6 G9\u0640\x00\u07d4-\xdf@\x90Wi\xbc\xc4&\xcb,)8\xff\xe0w\xe1\u8758\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4-\xe0\x96D\x00\u0082\xbd\u05ca\x91\x9ck\xf7|k_yay\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\xe3\x1a\xfd\x18\x9a\x13\xa7o\xf6\xfes\xea\xd9\xf7K\xb5\u0126)\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4-\xec\x982\x9d\x1f\x96\u00e5\x9c\xaay\x81uTR\xd4\xdaI\u0549\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94-\ue422\x8f\x19-gj\x87s#+V\xf1\x8f#\x9e/\xad\x8a\x03\xef\xa7\xe7G\xb6\u046d\x00\x00\xe0\x94.\b\x80\xa3E\x96#\a \xf0Z\xc8\xf0e\xaf\x86\x81\u0736\u008a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4.\fW\xb4qP\xf9Z\xa6\xa7\xe1j\xb9\xb1\xcb\xf5C(\x97\x9a\x89\x05k\xc7^-c\x10\x00\x00\u07d4.\x10\x91\v\xa6\xe0\xbc\x17\xe0UUf\x14\u02c7\t\x0fM~[\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94.$\xb5\x97\x87;\xb1A\xbd\xb27\xea\x8aZ\xb7Gy\x9a\xf0-\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.(\x10\xde\xe4J\xe4\xdf\xf3\xd8cB\xab\x12fW\xd6S\xc36\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.,\xbdz\xd8%G\xb4\xf5\xff\x8b:\xb5o\x94*dE\xa3\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4.-~\xa6k\x9fG\xd8\xccR\xc0\x1cR\xb6\u147c}G\x86\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4.C\x93H\u07caBw\xb2*v\x84W\xd1\x15\x8e\x97\xc4\t\x04\x89*\x1e\x9f\xf2o\xbfA\x00\x00\xe0\x94.F\xfc\xeej;\xb1E\xb5\x94\xa2C\xa3\x91?\xce]\xado\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794.G\xf2\x87\xf4\x98#7\x13\x85\r1&\x82<\xc6}\xce\xe2U\x88\u029d\x9e\xa5X\xb4\x00\x00\u07d4.N\u1b99j\xa0\xa1\xd9$(\xd0fR\xa6\xbe\xa6\xd2\xd1]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.R\x91+\xc1\x0e\xa3\x9dT\xe2\x93\xf7\xae\u05b9\x9a\x0fLs\xbe\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4.a\x9fW\xab\xc1\u91ea\x93j\xe3\xa2&Ib\xe7\xeb-\x9a\x89(\xfb\x9b\x8a\x8aSP\x00\x00\u07d4.d\xa8\xd7\x11\x11\xa2/L]\xe1\xe09\xb36\xf6\x8d9\x8a|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.i3T=O,\xc0\vSP\xbd\x80h\xba\x92C\u05be\xb0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94.~\x05\xe2\x9e\u0767\xe4\xae%\xc5\x175C\xef\xd7\x1fm=\x80\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4.\u007fFU \xec5\xcc#\u058eue\x1b\xb6h\x95D\xa1\x96\x898\xec[r\x1a\x1a&\x80\x00\u07d4.\x8e\xb3\nqn_\xe1\\t#>\x03\x9b\xfb\x11\x06\xe8\x1d\x12\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94.\x98$\xb5\xc12\x11\x1b\xca$\xdd\xfb\xa7\xe5u\xa5\xcdr\x96\xc1\x8a\x03\xa4\x84Qnm\u007f\xfe\x00\x00\u07d4.\xa5\xfe\xe6?3z7nK\x91\x8e\xa8!H\xf9MH\xa6&\x89e\x0f\x8e\r\u0493\xc5\x00\x00\u07d4.\xafN*F\xb7\x89\xcc\u0088\xc8\xd1\xd9)N?\xb0\x858\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4.\xaf\xf9\xf8\xf8\x110d\u04d5z\xc6\xd6\xe1\x1e\xeeB\xc8\x19]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4.\xba\fn\xe5\xa1\x14\\\x1cW9\x84\x96:`]\x88\nz \x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4.\xc9X\"\xeb\x88{\xc1\x13\xb4q*M\xfd\u007f\x13\xb0\x97\xb5\xe7\x8965\u026d\xc5\u07a0\x00\x00\u07d4.\xcaj<]\x9fD\x9d\tV\xbdC\xfa{M{\xe8CYX\x89lk\xdaip\x9c\xc2\x00\x00\xe0\x94.\xca\xc5\x04\xb23\x86n\xb5\xa4\xa9\x9e{\u0490\x13Y\xe4;=\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4.\xeb\xf5\x942\xb5(\x92\xf98\v\xd1@\xaa\x99\xdc\xf8\xad\f\x0f\x89\b=lz\xabc`\x00\x00\u07d4.\xee\xd5\x04q\xa1\xa2\xbfS\xee0\xb1#.n\x9d\x80\xef\x86m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4.\xefk\x14\x17\u05f1\x0e\xcf\xc1\x9b\x12:\x8a\x89\xe7>RlX\x89 \x86\xac5\x10R`\x00\x00\u07d4.\xf8i\xf05\vW\xd54x\xd7\x01\xe3\xfe\xe5)\xbc\x91\x1cu\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4.\xf9\xe4eqj\xca\u03f8\xc8%/\xa8\xe7\xbcyi\xeb\xf6\u4255\x9e\xb1\xc0\xe4\xae \x00\x00\xe0\x94.\xfcLd}\xacj\xca\xc3Uw\xad\"\x17X\xfe\xf6ao\xaa\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4/\x13eu&\xb1w\xca\xd5G\u00d0\x8c\x84\x0e\xffd{E\u0649?v\x84\x9c\xf1\xee,\x80\x00\u07d4/\x18}ZpMZ3\x8c[(v\xa0\x90\xdc\xe9d(N)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94/%#\u0303O\x00\x86\x05$\x02bb\x96gQ\x86\xa8\u508a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4/(*\xbb\xb6\u0523\xc3\xcd;\\\xa8\x12\xf7d>\x800_\x06\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4/+\xba\x1b\x17\x96\x82\x1avo\xced\xb8O(\xech\xf1Z\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4/1]\x90\x16\xe8\xee_Sf\x81 /\x90\x84\xb02TMM\x898<\xd1+\x9e\x86<\x00\x00\u07d4/M\xa7SC\x0f\xc0\x9es\xac\xbc\xcd\xcd\xe9\xdad\u007f+]7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/P\x80\xb8?~-\xc0\xa1\xdd\x11\xb0\x92\xad\x04+\xffx\x8fL\x89\xb4\xf8\xfby#\x1d+\x80\x00\u07d4/a\uf941\x9dp_+\x1eN\xe7T\xae\xb8\xa8\x19Pju\x89O%\x91\xf8\x96\xa6P\x00\x00\xe0\x94/f\xbf\xbf\"b\xef\u030d+\xd0DO\u0170ib\x98\xff\x1e\x8a\x02\x1a\xd95\xf7\x9fv\xd0\x00\x00\u07d4/m\xce\x130\u015e\xf9!`!TW-MK\xac\xbd\x04\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4/}2\x90\x85\x1b\xe5\u01b4\xb4?}Et2\x9fa\xa7\x92\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4/\x858\x17\xaf\u04f8\xf3\xb8n\x9f`\xeew\xb5\xd9ws\xc0\xe3\x89N\xae\xeaD\xe3h\xb9\x00\x00\u07d4/\xa4\x91\xfbY \xa6WN\xbd(\x9f9\xc1\xb2C\r-\x9aj\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xb5f\xc9K\xbb\xa4\xe3\xcbg\xcd\xda}_\xadq1S\x91\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4/\xbbPJ]\xc5'\xd3\xe3\xeb\x00\x85\xe2\xfc<}\xd58\xcbz\x89C\u00b1\x8a\xec<\n\x80\x00\u07d4/\xbc\x85y\x8aX5\x98\xb5\"\x16mn\x9d\xda\x12\x1db}\xbc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4/\xbc\xef3\x84\xd4 \xe4\xbfa\xa0f\x99\x90\xbcpT\U00065bc9lk\x93[\x8b\xbd@\x00\x00\xe0\x94/\xc8.\xf0v\x93#A&Oaz\f\x80\xddW\x1ej\xe99\x8a\x01\x84$\xf5\xf0\xb1\xb4\xe0\x00\x00\u07d4/\u075by\u07cd\xf50\xadc\xc2\x0eb\xafC\x1a\xe9\x92\x16\xb8\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4/\xe0\x02?W\"e\x0f:\x8a\xc0\x10\t\x12^t\xe3\xf8.\x9b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4/\xe0\xccBKS\xa3\x1f\t\x16\xbe\b\xec\x81\xc5\v\xf8\xea\xb0\xc1\x89 \x86\xac5\x10R`\x00\x00\u07d4/\xe1:\x8d\a\x85\u0787X\xa5\xe4\x18v\xc3n\x91l\xf7Pt\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4/\xea\x1b/\x83O\x02\xfcT3?\x8a\x80\x9f\x048\xe5\x87\n\xa9\x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d4/\xee6\xa4\x9e\xe5\x0e\xcfqo\x10G\x91VFw\x9f\x8b\xa0?\x899B\"\xc4\u0686\xd7\x00\x00\u07d4/\xef\x81G\x8aK.\x80\x98\xdb_\xf3\x87\xba!S\xf4\xe2+y\x896'\xe8\xf7\x127<\x00\x00\u07d4/\xf1`\xc4Or\xa2\x99\xb5\xec-q\xe2\x8c\xe5Dm/\u02ef\x89\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4/\xf1\xcaU\xfd\x9c\xec\x1b\x1f\xe9\U00029af7LQ<\x1e*\xaa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94/\xf5\u02b1,\r\x95\u007f\xd33\xf3\x82\xee\xb7Q\a\xa6L\xb8\xe8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94/\xf80\xcfU\xfb\x00\u0560\xe05\x14\xfe\xcdD1K\xd6\xd9\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4/\xfe\x93\xec\x1aV6\xe9\xee4\xafp\xdf\xf5&\x82\xe6\xffpy\x89lk\x93[\x8b\xbd@\x00\x00\u07d40\x03y\x88p&q\xac\xbe\x89,\x03\xfeW\x88\xaa\x98\xaf(z\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d40$\x8dX\xe4\x14\xb2\x0f\xed:lH+Y\xd9\xd8\xf5\xa4\xb7\xe2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4019\xbcYd\x03\xd5\u04d3\x1fwLf\u013aFtT\u06c9\\%\xe1J\xea(?\x00\x00\u079408\x00\x87xie\x14\x9e\x81B;\x15\xe3\x13\xba2\xc5\u01c3\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d40:0\xacB\x86\xae\x17\xcfH=\xad{\x87\fk\xd6M{J\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40?\xba\xeb\xbeF\xb3[n[t\x94j_\x99\xbc\x15\x85\xca\xe7\x89/\x9a\xc0i_[\xba\x00\x00\u07d40ADZ3\xba\x15\x87A\x16\r\x9c4N\xb8\x8e\\0o\x94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d40H\x01d\xbc\xd8It\xeb\xc0\xd9\f\x9b\x9a\xfa\xb6&\xcd\x1cs\x89+^:\xf1k\x18\x80\x00\x00\u07d40N\u019atTW!\xd71j\xefM\u03f4\x1a\u015e\xe2\xf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x940Q\x182\x91\x8d\x804\xa7\xbe\xe7.\xf2\xbf\xeeD\x0e\u02fc\xf6\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d40Q?\u029f6\xfdx\x8c\xfe\xa7\xa3@\xe8m\xf9\x82\x94\xa2D\x89\x18;_\x03\xb1G\x9c\x00\x00\u07d40U\xef\xd2`)\xe0\xd1\x1b\x93\r\xf4\xf5;\x16,\x8c?\xd2\u0389\x1b\x1a\b\x927\a=\x00\x00\u07d40]&\xc1\v\xdc\x10?k\x9c!'.\xb7\xcb-\x91\b\xc4~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d40_x\xd6\x18\xb9\x90\xb4)[\xac\x8a-\xfa&(\x84\xf8\x04\xea\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x940d\x89\x9a\x96<Gy\xcb\xf6\x13\xcdi\x80\x84j\xf1\xe6\xece\x8a\x01{w<\xe6\xe5\xdf\n\x00\x00\u07d40s\x04f\xb8\xebm\xc9\rT\x96\xaav\xa3G-}\xbe\v\xbe\x89lh\xcc\u041b\x02,\x00\x00\u07d40t,\xcd\xf4\xab\xbc\xd0\x05h\x1f\x81Y4\\\x9ey\x05K\x1a\x89$=M\x18\"\x9c\xa2\x00\x00\xe0\x940\x83\xef\x0e\xd4\xc4@\x11\x96wJ\x95\xcfN\u0703\xed\xc1HO\x8a#\xff\xb7\xedee\xd6@\x00\x00\u07d40\x8d\xd2\x1c\xeb\xe7U\x12g\x04\xb4\x8c\x0f\r\xc24\xc6\v\xa9\xb1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d40\x90\xf8\x13\x0e\xc4Df\xaf\xad\xb3n\xd3\xc9&\x139cg{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d40\x95D\xb6#,=\xd77\xf9E\xa01\x93\u045b_?e\xb9\x89:\xf3B\xf6~\xf6\xc8\x00\x00\u07d40\x96\u0723A\b\b[\xcf\x04\xaer\xb9Et\xa1>\x1a>\x1d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d40\x98\xb6]\xb9>\xca\xca\xf75<H\x80\x83\x90\xa2#\xd5v\x84\x89\x18d\x84\xcf{\xb6\xa4\x80\x00\u07d40\xa9\xdarWLQ\xe7\xee\t\x04\xba\x1fs\xa6\xb7\xb8;\x9b\x9d\x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d40\xac\xd8X\x87_\xa2N\xef\rW/\xc7\xd6*\xad\x0e\xbd\xdc5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d40\xb6aP\xf1\xa64W\x02?\xddE\xd0\xccl\xb5N\f\x0f\x06\x8965\u026d\xc5\u07a0\x00\x00\u07d40\xbbCW\xcdi\x10\xc8m\"8\xbfr|\xbe\x81Vh\x0eb\x89\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d40\xbfa\xb2\xd8w\xfe\x10cQ&2o\xa1\x89\u4c31\u00f0\x897\xb4\x89\x85\xa5\xd7\xe6\x00\x00\u07d40\xc0\x11B\x90z\xcb\x15e\xf7\x048\xb9\x98\n\xe71\x81\x878\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x940\xc2j\x8e\x97\x1b\xaa\x18U\xd63\xbap?\x02\x8c\u01c71@\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d40\xdbk\x9b\x10~b\x10/CJ\x9d\u0416\f !\xf5\xceL\x89 \x83\x17\x9bnBS\x00\x00\u07d40\xe33X\xfc!\xc8P\x06\xe4\x0f25}\u0209Y@\xaa\xf0\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d40\xe6\t\x00\xca\xccr\x03\xf3\x14\xdc`CG%Qg\xfc*\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d40\u7273\xd2F^\x94nb\x10\xfa[5\xdeN\x8c\x93\b_\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x940\xe9i\x8c\xf1\xe0\x8a\x9d\x04\x8b\xd8\xd8\x04\x8f(\xbe~\xd9@\x9f\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d40\xe9\u0560\b\x8f\x1d\xdb/\u04c0\xe2\xa0I\x19\"f\xc5\x1c\xbf\x89\n\xac\xac\u0679\xe2+\x00\x00\u07d40\xea\xc7@\xe4\xf0,\xb5n\xef\x05&\xe5\xd3\x002&\x00\xd0>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d40\uc4d2$J!\b\u0247\xbc\\\xdd\xe0\ud7c3z\x81{\x89T\x99%\xf6\xc9\xc5%\x00\x00\xe0\x940\xed\x11\xb7{\xc1~^f\x94\u023c[nG\x98\xf6\x8d\x9c\xa7\x8a\x1eo\xb3B\x1f\xe0)\x9e\x00\x00\u07d40\xf7\xd0%\xd1o{\xee\x10U\x80Ho\x9fV\x1c{\xae?\xef\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x940\xfb\xe5\x88_\x9f\xcc\xe9\xea^\u06c2\xedJ\x11\x96\xdd%\x9a\xed\x8a\x01\x19\xe4\u007f!8\x1f@\x00\x00\u07d41\x04}p?c\xb94$\xfb\xbdn/\x1f\x9et\xde\x13\xe7\t\x89\x9a\x81f\xf7\u6ca7\x80\x00\u07d411?\xfdc[\xf2\xf32HA\xa8\x8c\a\xed\x14aD\xce\xeb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d41Y\xe9\fH\xa9\x15\x90J\xdf\u24b2/\xa5\xfd^ryk\x896\xaf\xe9\x8f&\x06\x10\x00\x00\u07d41]\xb7C\x9f\xa1\u0574#\xaf\xa7\xddq\x98\xc1\xcft\xc9\x18\xbc\x89 \x86\xac5\x10R`\x00\x00\u07d41^\xf2\xdab\x0f\xd30\xd1.\xe5]\xe5\xf3)\xa6\x96\xe0\xa9h\x89\b!\xab\rD\x14\x98\x00\x00\u07d41n\x92\xa9\x1b\xbd\xa6\x8b\x9e/\x98\xb3\xc0H\x93N<\xc0\xb4\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d41n\xb4\xe4}\xf7\x1bB\xe1mo\xe4h%\xb72{\xaf1$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41q\x87~\x9d\x82\f\xc6\x18\xfc\t\x19\xb2\x9e\xfd3?\xdaI4\x8965\u026d\xc5\u07a0\x00\x00\u07d41|\xf4\xa2<\xb1\x91\xcd\xc5c\x12\u009d\x15\xe2\x10\xb3\xb9\xb7\x84\x89\a\xcef\xc5\x0e(@\x00\x00\u07d41\x8b.\xa5\xf0\xaa\xa8y\xc4\xd5\xe5H\xac\x9d\x92\xa0\xc6t\x87\xb7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d41\x8cv\xec\xfd\x8a\xf6\x8dpUSR\xe1\xf6\x01\xe3Y\x88\x04-\x89\x1b1\x19.h\xc7\xf0\x00\x00\u07d41\x8f\x1f\x8b\xd2 \xb0U\x8b\x95\xfb3\x10\x0f\xfd\xbbd\r|\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d41\xaa;\x1e\xbe\x8cM\xbc\xb6\xa7\b\xb1\xd7H1\xe6\x0eIv`\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d41\xab\b\x89f\xec\xc7\"\x92X\xf6\t\x8f\xceh\xcf9\xb3\x84\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x941\xadM\x99F\xef\t\xd8\xe9\x88\xd9F\xb1\"\u007f\x91A\x90\x176\x8a\x04\xd8S\xc8\xf8\x90\x89\x80\x00\x00\xe0\x941\xb4;\x01]\x00\x81d<l\xdaF\xa7\a:m\xfd\xbc\xa8%\x8a\n\x97\x91e \xcd\x18\xe8\x00\x00\u07d41\xcc\xc6\x16\xb3\x11\x82h\xe7]\x9a\xb8\x99l\x88X\xeb\xd7\xf3\u00c9\x15\xae\x0fw\x1c\xa1R\x00\x00\u07d41\xd8\x1dRl\x19^?\x10\xb5\xc6\xdbR\xb5\xe5\x9a\xfb\u0a55\x89\x0eO\xbciD\x9f \x00\x00\u07d41\xe9\xc0\x0f\f jNN~\x05\"\x17\r\xc8\x1e\x88\xf3\xebp\x89\x91\x8d\xdc:B\xa3\xd4\x00\x00\u07d41\xea\x12\u051a5\xa7@x\r\xde\xea\xec\xe8L\b5\xb2bp\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d41\xean\xab\x19\xd0\ad\xe9\xa9^\x18?+\x1b\"\xfc}\xc4\x0f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d41\xeb\x12<\x95\xc8+\xf6\x85\xac\xe7\xa7Z\x18\x81\xa2\x89\xef\xca\x10\x891\xe0\t`sq\xbd\x00\x00\u07d41\ud147\x88\xbd\xa4\xd5'\t\x92\"\x1c\xc0B\x06\xecba\r\x89?\xc0GIH\xf3`\x00\x00\u07d41\xf0\x06\xf3IN\xd6\xc1n\xb9*\xaf\x90D\xfa\x8a\xbb_\u0563\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x942\x01%\x9c\xafsJ\xd7X\x1cV\x10Q\xba\v\xca\u007f\u0594k\x8a&\x1d\xd1\xce/ \x88\x80\x00\x00\u07d42\x03N\x85\x81\xd9HN\x8a\xf4*(\xdf\x19\x012\xec)\xc4f\x89\xbb\x91%T\"c\x90\x00\x00\u07d42 !\x02&x\xa0\x16m K:\xaaz\xd4\xecK\x88\xb7\u0409\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d42%\xc1\xca_*\x9c\x88\x15k\xb7\xd9\xcd\xc4J2fS\xc2\x14\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d42'\x88\xb5\xe2\x9b\xf4\xf5\xf5Z\xe1\u0773 \x85\xfd\xa9\x1b\x8e\xbe\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d42-o\x9a\x14\r!?L\x80\xcd\x05\x1a\xfe%\xc6 \xbfL}\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d42.\\C\xb0\xf5$8\x96U\xa9\xb3\xff$\xf2\xd4\xdb=\xa1\x0f\x89\xfc\x13\xb6\x9b>~h\x00\x00\u07d424\x86\xcad\xb3uGO\xb2\xb7Y\xa9\xe7\xa15\x85\x9b\xd9\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d427I\xa3\xb9q\x95\x9eF\u0234\x82-\xca\xfa\xf7\xaa\xf9\xbdn\x89\x01\x16q\xa5\xb2Ep\x00\x00\u07d42:\xadA\xdfKo\xc8\xfe\u038c\x93\x95\x8a\xa9\x01\xfah\bC\x894\x95tD\xb8@\xe8\x00\x00\xe0\x942;<\xfe>\xe6+\xbd\xe2\xa2a\xe5<\xb3\xec\xc0X\x10\xf2\u018a\x02\ub3b1\xa1r\u0738\x00\x00\u07d42?\xca^\xd7\u007fi\x9f\x9d\x990\xf5\xce\xef\xf8\xe5oY\xf0<\x89Hz\x9a0E9D\x00\x00\u07d42H\\\x81\x87(\xc1\x97\xfe\xa4\x87\xfb\xb6\xe8)\x15\x9e\xba\x83p\x899!\xb4\x13\xbcN\xc0\x80\x00\xe0\x942P\xe3\xe8X\xc2j\xde\u032d\xf3jVc\xc2*\xa8LAp\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x942Y\xbd/\xdd\xfb\xbco\xba\u04f6\xe8t\xf0\xbb\xc0,\xda\x18\xb5\x8a\x02\x84`VI[\r\x18\x80\x00\u07d42uIo\xd4\u07491\xfdi\xfb\n\v\x04\xc4\xd1\xff\x87\x9e\xf5\x89\x18-~L\xfd\xa08\x00\x00\u07d42{\xb4\x9euOo\xb4\xf73\xc6\xe0o9\x89\xb4\xf6]K\xee\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d42\x82y\x1do\xd7\x13\xf1\xe9OK\xfdV^\xaax\xb3\xa0Y\x9d\x89Hz\x9a0E9D\x00\x00\u07d42\x83\xeb\u007f\x917\xdd9\xbe\xd5_\xfek\x8d\xc8E\xf3\xe1\xa0y\x89\x03\x97\n\xe9!Ux\x00\x00\u07d42\x86\t\x97\xd70\xb2\xd8;s$\x1a%\xd3f}Q\xc9\b\xef\x89\x1b\x1a\b\x927\a=\x00\x00\xe0\x942\x86\u047cez1,\x88G\xd9<\xb3\xcbyP\xf2\xb0\xc6\xe3\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x942\xa2\r\x02\x8e,b\x18\xb9\xd9[D\\w\x15$cj\"\xef\x8a\x02\x02\xfe\xfb\xf2\xd7\xc2\xf0\x00\x00\u07d42\xa7\x06\x91%\\\x9f\xc9y\x1aOu\u0238\x1f8\x8e\n%\x03\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d42\xb7\xfe\xeb\xc5\u015b\xf6^\x86\x1cL\v\xe4*v\x11\xa5T\x1a\x89w\u9aa8R\\\x10\x00\x00\xe0\x942\xba\x9a}\x04#\xe0:R_\xe2\xeb\xebf\x1d \x85w\x8b\u060a\x04<3\xc1\x93ud\x80\x00\x00\u07d42\xbb.\x96\x93\xe4\xe0\x854M/\r\xbdF\xa2\x83\u3807\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x942\xc2\xfd\u2daa\xbb\x80\u5ba2\xb9I\xa2\x17\xf3\xcb\t\"\x83\x8a\x010a`\xaf\xdf 7\x80\x00\u07d42\xd9P\xd5\xe9>\xa1\u0574\x8d\xb4qO\x86{\x03 \xb3\x1c\x0f\x897\b\xba\xed=h\x90\x00\x00\u07d42\u06f6qlT\xe81e\x82\x9aJ\xbb6uxI\xb6\xe4}\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xebd\xbe\x1b]\xed\xe4\b\u01bd\xef\xben@\\\x16\xb7\xed\x02\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d42\xef\\\xdcg\x1d\xf5V*\x90\x1a\xee]\xb7\x16\xb9\xbev\xdc\xf6\x89lk\x93[\x8b\xbd@\x00\x00\u07d42\xf2\x9e\x87'\xa7LkC\x01\xe3\xff\xff\x06\x87\xc1\xb8p\xda\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d42\xfa\x0e\x86\xcd\b}\u058di1\x90\xf3-\x931\t\t\xedS\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d42\xfb\xee\xd6\xf6&\xfc\xdf\xd5\x1a\xca\xfbs\v\x9e\xef\xf6\x12\xf5d\x89lk\x93[\x8b\xbd@\x00\x00\u07943\x00\xfb\x14\x9a\xde\xd6[\u02e6\xc0N\x9c\u05b7\xa0;\x89;\xb1\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x943\x01\xd9\xca/;\xfe\x02by\xcdh\x19\xf7\x9a)=\x98\x15n\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x943\b\xb04f\xc2z\x17\xdf\xe1\xaa\xfc\xeb\x81\xe1m)4Vo\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07943\x1a\x1c&\xcci\x94\xcd\xd3\xc1K\xec\xe2v\xff\xffK\x9d\xf7|\x88\xfaz\xed\xdfO\x06\x80\x00\xe0\x943&\xb8\x8d\xe8\x06\x18DT\xc4\v'\xf3\t\xd9\xddm\u03f9x\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x943)\xeb;\xafCE\xd6\x00\xce\xd4\x0en\x99ueo\x117B\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d432\r\xd9\x0f+\xaa\x11\r\xd34\x87*\x99\x8f\x14\x84&E<\x8965f3\xeb\xd8\xea\x00\x00\u07d436\xc3\xefn\x8bP\xee\x90\xe07\xb1d\xb7\xa8\xea_\xaa\xc6]\x89\x0e\u0223\xa7\x1c\"T\x00\x00\xe0\x9438\fo\xffZ\xcd&Q0\x96)\u06daq\xbf? \u017a\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d43:\xd1Yd\x01\xe0Z\xea-6\xcaG1\x8e\xf4\xcd,\xb3\u07c9\x9d\xc0\\\xce(\u00b8\x00\x00\u07d43C@\xeeK\x9c\u0701\xf8P\xa7Q\x16\xd5\x0e\u9d98%\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d43H\x1e\x85n\xbe\u050e\xa7\b\xa2t&\xef(\xe8g\xf5|\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943V[\xa9\xda,\x03\xe7x\xce\x12)O\b\x1d\xfe\x81\x06M$\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07943X\x1c\xee#0\x88\xc0\x86\r\x94N\f\xf1\u03ab\xb8&\x1c.\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d43XX\xf7I\xf1i\u02bc\xfeR\xb7\x96\xe3\xc1\x1e\xc4~\xa3\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x943^\"\x02[zw\u00e0t\u01cb\x8e=\xfe\a\x13A\x94n\x8a\x02'\xcas\n\xb3\xf6\xac\x00\x00\u07d43b\x9b\xd5/\x0e\x10{\xc0q\x17ld\xdf\x10\x8fdw}I\x89\x01\xcf\xddth!n\x80\x00\u07d43{;\u07c6\xd7\x13\xdb\xd0{]\xbf\xcc\x02+z{\x19F\xae\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d43|\xfe\x11W\xa5\u0191 \x10\xddV\x153y\x17i\u00b6\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xb36\xf5\xba^\xdb{\x1c\xcc~\xb1\xa0\u0644\xc1#\x1d\x0e\u0709lk\x93[\x8b\xbd@\x00\x00\u07d43\xc4\a\x13;\x84\xb3\xcaL=\xed\x1fFX\x90\f8\x10\x16$\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\xe0\x943\xd1r\xab\a\\Q\xdb\x1c\xd4\n\x8c\xa8\xdb\xff\r\x93\xb8C\xbb\x8a\x016x\x05\x10\xd1-\xe3\x80\x00\u07d43\xe9\xb7\x18#\x95.\x1ff\x95\x8c'\x8f\u008b\x11\x96\xa6\u0164\x89\x05k\xc7^-c\x10\x00\x00\u07d43\xeakxU\xe0[\a\xab\x80\u06b1\xe1M\xe9\xb6I\xe9\x9bl\x89\x1c\xd6\xfb\xadW\xdb\xd0\x00\x00\u07d43\xf1R#1\rD\u078bf6h_:L=\x9cVU\xa5\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d43\xf4\xa6G\x1e\xb1\xbc\xa6\xa9\xf8[;Hr\xe1\aU\xc8+\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d43\xfbWzM!O\xe0\x10\xd3,\xca|>\xed\xa6?\x87\xce\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d43\xfdq\x8f\v\x91\xb5\xce\u020a]\xc1^\xec\xf0\xec\xef\xa4\xef=\x89\x17r$\xaa\x84Lr\x00\x00\u07d44\x14\x80\u030c\xb4v\xf8\xd0\x1f\xf3\b\x12\xe7\xc7\x0e\x05\xaf\xaf]\x89lk\x93[\x8b\xbd@\x00\x00\u07d44'-^ut1]\xca\u9afd1{\xac\x90(\x9dGe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x9440\xa1c\x81\xf8i\xf6\xeaT#\x91XU\xe8\x00\x885%\xa9\x8a\x03\xca\\f\u067cD0\x00\x00\u07d441\x86%\x81\x8e\xc1?\x11\x83Z\xe9sS\xce7}oY\n\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d449<]\x91\xb9\xdeYr\x03\xe7[\xacC\t\xb5\xfa=(\u00c9\n\x84Jt$\xd9\xc8\x00\x00\u07d449\x99\x8b$|\xb4\xbf\x8b\xc8\nm+5'\xf1\xdf\xe9\xa6\u0489\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d44C}\x14ed\v\x13l\xb5\x84\x1c?\x93O\x9b\xa0\xb7\t}\x89\t`\xdbwh\x1e\x94\x00\x00\u07d44J\x8d\xb0\x86\xfa\xedN\xfc7\x13\x1b:\"\xb0x-\xadp\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x944fM\"\x0f\xa7\xf3yX\x02J32\u0584\xbc\xc6\xd4\u023d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44f\xf6~9cl\x01\xf4;:!\xa0\xe8R\x93%\xc0\x86$\x89-\xb1\x16vP\xac\xd8\x00\x00\u07d44\x856\x1e\xe6\xbf\x06\xefe\b\xcc\xd2=\x94d\x1f\x81M>/\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x85\xf6!%d3\xb9\x8aB\x00\xda\xd8W\xef\xe5Y7\uc609lk\x93[\x8b\xbd@\x00\x00\u07d44\x95\x8aF\xd3\x0e0\xb2s\xec\xc6\xe5\xd3X\xa2\x12\xe50~\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d44\x97\xddf\xfd\x11\x80q\xa7\x8c,\xb3n@\xb6e\x1c\xc8%\x98\x89\x05\xf1\x01kPv\xd0\x00\x00\xe0\x944\x9a\x81k\x17\xab='\xbb\xc0\xae\x00Q\xf6\xa0p\xbe\x1f\xf2\x9d\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d44\x9d,\x91\x8f\u041e(\a1\x8ef\xceC)\t\x17k\xd5\v\x89<\xb7\x1fQ\xfcU\x80\x00\x00\u07d44\xa0C\x1f\xff^\xad\x92\u007f<id\x96\x16\xdcn\x97\x94_o\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x944\xa8]m$?\xb1\u07f7\xd1\xd2\xd4OSn\x94zL\ue78a\x04<3\xc1\x93ud\x80\x00\x00\u07d44\xa9\x01\xa6\x9f\x03k\u03dfxC\xc0\xba\x01\xb4&\xe8\xc3\xdc+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d44\xb4TAn\x9f\xb4'Nj\xdd\xf8SB\x8a\x01\x98\xd6.\xe1\x89\x16\x10Bw\x9f\x1f\xfc\x00\x00\u07d44\xc8\xe5\xf13\x0f\xcbK\x14\xcau\xcb%\x80\xa4\xb9= N6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x944\u211b\xeaX:\xb0\xcc7\x97Q\x90\xf3\"\xb3\x95\x05U\x82\x8a\x01\xa5\xc5\xe8W\xfd\xf2\xb2\x00\x00\u07d44\xfaw\x92\xba\u063b\xd7\xffd\x05b\x14\xa3>\xb6`\f\x1e\xa8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d44\xff&\xeb`\xa8\u0469ZH\x9f\xae\x13n\xe9\x1dNX\bL\x89 \x86\xac5\x10R`\x00\x00\u07d44\xffX)R\xff$E\x8f{\x13\xd5\x1f\vO\x98p\"\xc1\xfe\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d45\x10k\xa9N\x85c\u0533\xcb<\\i,\x10\xe6\x04\xb7\xce\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x945\x14_b\x03\x97\u019c\xb8\xe0\tb\x96\x1f\x0fH\x86d9\x89\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d45\x14t0\xc3\x10e\x00\u77e2\xf5\x02F.\x94p<#\xb1\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x945\x17\x87\x845\x05\xf8\xe4\xef\xf4ef\xcc\u695fM\x1c_\xe7\x8a\x01\xf5q\x89\x87fKH\x00\x00\xe0\x945\x1f\x16\xe5\xe0sZ\xf5gQ\xb0\xe2%\xb2B\x11q9@\x90\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\xe0\x945$\xa0\x00#N\xba\xaf\a\x89\xa14\xa2\xa4\x178<\xe5(*\x8a\x011yU\x94}\x8e,\x00\x00\u07d45&\xee\xce\x1ak\xdc>\xe7\xb4\x00\xfe\x93[HF?1\xbe\u05c9\x04w\x87\x9bm\x140\x00\x00\u07d45*x_J\x92\x162PL\xe5\xd0\x15\xf8<I\xaa\x83\x8dm\x89\xe9\xe7\xe0\xfb5\xb7x\x00\x00\xe0\x945-)\xa2n\x8aA\x81\x81\x81tdg\xf5\x82\xe6\xe8@\x12\xe0\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d45.w\xc8ain\xf9j\xd5I4\xf8\x94\xaa\x8e\xa3QQ\u074965\u026d\xc5\u07a0\x00\x00\xe0\x945/%\xba\xbfJi\x06s\xe3Q\x95\xef\xa8\xf7\x9d\x05\x84\x8a\xad\x8a\x0e%<9\xben}\xc0\x00\x00\u07d456E3\"\xc1Fl\xb9\x05\xaf\\3\\\xa8\xdbt\xbf\xf1\xe6\x89\x18;_\x03\xb1G\x9c\x00\x00\u07d45=\xbe\xc4/\x92\xb5\x0f\x97Q)\xb9<L\x99su\xf0\x90s\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d45@\u01fdz\x84B\u057e\xe2\x1a!\x80\xa1\xc4\xed\xff\x16I\xe0\x89C.\xacLo\x05\xb9\x80\x00\u07d45I\xbd@\xbb\xbc+0\t\\\xac\x8b\xe2\xc0z\x05\x88\xe0\xae\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d45R\xa4\x96\xeb\xa6\u007f\x12\xben\xed\xab6\f\xd16a\xdct\x80\x89\x10CV\x1a\x88)0\x00\x00\u07d45T\x94{{\x94{\x00@\xdaR\xca\x18\t%\xc6\u04f8\x8f\xfe\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d45\\\f9\xf5\xd5p\vA\xd3u\xb3\xf1xQ\xdc\xd5$\x01\xf9\x89\u05f3\xb7\xbaZ\xbfL\x00\x00\u07d45\\\xcf\xe0\xe7}U{\x97\x1b\xe1\xa5X\xbc\x02\u07de\xee\x05\x94\x89_\\\xb1\xaf\xc8e(\x00\x00\xe0\x945q\xcfz\xd3\x04\xec\xae\xe5\x95y/K\xbf\xa4\x84A\x85I\u058a\x01;\xcd\r\x89-\x9e\x16\x00\x00\u07d45u\xc7pf\x8a\x9d\x17\x9f\x1e\xf7h\u0093\xf8\x01f\xe2\xaa=\x89\x19\xb2\x12H\xa3\xef(\x00\x00\u07d45z\x02\xc0\xa9\xdf\xe2\x87\xdeD\u007f\xb6zp\xec[b6fG\x89\x01s\x17\x90SM\xf2\x00\x00\u07d45\x85^\xc6A\xab\x9e\b\x1e\xd0\u00a6\xdc\xd8\x13T\xd0$J\x87\x89A'\xab\u94e7\xaa\x80\x00\u07d45\x88\x89Z\xc9\xfb\xaf\xec\x01 \x92\xdc\x05\xc0\xc3\x02\xd9\a@\xfa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d45\x99I<\xe6Wr\u03d3\xe9\x8a\xf1\x19^\xc0\x95]\u0240\x02\x89QQY\fg\xb3(\x00\x00\xe0\x945\xa0\x80\x81y\x91s\xe0\x01\xcc[\xd4j\x02@m\xc9]\x17\x87\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d45\xa5I\xe8\xfdl6\x8dm\u0326\xd2\xe7\u044eM\xb9_R\x84\x89\x1b\x1a\b\x927\a=\x00\x00\u07d45\xa6\x88P\x83\u0219\u06bb\xf50\xedl\x12\xf4\xdd: L\xf5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d45\xaa\xa0F]\x1c&\fB\x0f\xa3\x0e&)\x86\x9f\xb6U\x92\a\x89&7\x81\xe0\xe0\x87\xc8\x00\x00\u07d45\xac\x1d>\xd7FO\xa3\xdb\x14\xe7r\x92\x13\u03aa7\x8c\t^\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d45\xaf\x04\n\f\xc23zv\xaf(\x81T\xc7V\x1e\x1a#3I\x8965\u026d\xc5\u07a0\x00\x00\u07d45\xb0>\xa4$W6\xf5{\x85\xd2\xebyb\x8f\x03m\xdc\xd7\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xbd$he\xfa\xb4\x90\xac\bz\xc1\xf1\xd4\xf2\xc1\r\f\xda\x03\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x945\xbff\x88R/5Fz\u007fu0#\x14\xc0+\xa1v\x80\x0e\x8a\x03\xafA\x82\x02\xd9T\xe0\x00\x00\u07d45\u022d\xc1\x11%C+;w\xac\xd6F%\xfeX\xeb\xee\x9df\x89lk\x93[\x8b\xbd@\x00\x00\u07d45\u0497\x0fI\xdc\xc8\x1e\xa9\xeep~\x9c\x8a\n\xb2\xa8\xbbtc\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d45\xe0\x96\x12\r\xea\xa5\xc1\xec\xb1d^,\u02cbN\xdb\xd9)\x9a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d45\xea!c\xa3\x8c\u07da\x12?\x82\xa5\xec\x00%\x8d\xae\v\xc7g\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d45\xf1\xda\x12{\x837o\x1b\x88\xc8*3Y\xf6z^g\xddP\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d45\xf2\x94\x9c\xf7\x8b\xc2\x19\xbbO\x01\x90|\xf3\xb4\xb3\u04c6T\x82\x89\x0f\xb5\xc8l\x92\xe44\x00\x00\u07d45\xf5\x86\x01I\xe4\xbb\xc0K\x8a\u0172r\xbeU\xad\x1a\xcaX\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x02E\x8d\xa8omj\x9d\x9e\xb0=\xaf\x97\xfeV\x19\xd4B\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x057-\x93\xa9\x01\t\x88\x01\x8f\x9f1]\x03.\u0448\x0f\xa1\x89\x1b\x1b\xcfQ\x89j}\x00\x00\u07d46\x16\xd4H\x98_]2\xae\xfa\x8b\x93\xa9\x93\xe0\x94\xbd\x85I\x86\x89\v\"\u007fc\xbe\x81<\x00\x00\u07d46\x16\xfbF\xc8\x15x\xc9\xc8\xebM;\xf8\x80E\x1a\x887\x9d}\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d46\x1cu\x93\x16\x96\xbc=B}\x93\xe7lw\xfd\x13\xb2A\xf6\xf4\x89\x1d\xc5\xd8\xfc&m\xd6\x00\x00\u07d46\x1d\x9e\xd8\v[\xd2|\xf9\xf1\"o&u2X\xee_\x9b?\x89\xbfi\x14\xba}r\xc2\x00\x00\u07d46\x1f;\xa9\xed\x95kw\x0f%}6r\xfe\x1f\xf9\xf7\xb0$\f\x89 \x86\xac5\x10R`\x00\x00\u07d46\"|\u07e0\xfd;\x9d~jtF\x85\xf5\xbe\x9a\xa3f\xa7\xf0\x89\n\xc2s\x0e\xe9\xc6\xc1\x80\x00\u07d46/\xbc\xb1\x06b7\n\x06\x8f\xc2e&\x02\xa2Wy7\xcc\xe6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d460\xc5\xe5e\u03aa\x8a\x0f\x0f\xfe2\x87^\xae*l\xe6<\x19\x89\t7r+7t\xd0\x00\x00\u07d463\x9f\x84\xa5\u00b4L\xe5=\xfd\xb6\xd4\xf9}\xf7\x82\x12\xa7\u07c9\x11o\x18\xb8\x17\x15\xa0\x00\x00\u07d464:\xec\xa0{n\u054a\x0eb\xfaN\xcbI\x8a\x12O\xc9q\x89\x10CV\x1a\x88)0\x00\x00\u07d46au@4\x81\xe0\xab\x15\xbbQF\x15\u02f9\x89\xeb\u018f\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d46ro;\x88Z$\xf9)\x96\u0681b^\u022d\x16\xd8\xcb\xe6\x89S\xafu\u0441HW\x80\x00\xe0\x946s\x95C\x99\xf6\u07feg\x18\x18%\x9b\xb2x\xe2\xe9.\xe3\x15\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d46u\x8e\x04\x9c\u064b\u03a1\"w\xa6v\xf9)sb\x89\x00#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d46\u007fY\u0302yS)8NA\xe1(1\x15\xe7\x91\xf2j\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x81\x0f\xf9\xd2\x13\xa2q\xed\xa2\xb8\xaay\x8b\xe6T\xfaK\xbe\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d46\x8cT\x14\xb5k\x84U\x17\x1f\xbf\ab \xc1\u02e4\xb5\xca1\x89\x1e>\xf9\x11\xe8=r\x00\x00\xe0\x946\x90$k\xa3\xc8\x06y\xe2.\xacD\x12\xa1\xae\xfc\xe6\xd7\u0342\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\x92\x8bU\xbc\x86\x15\t\xd5\x1c\x8c\xf1\xd5F\xbf\xecn>\x90\xaf\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d46\x98\"\xf5W\x8b@\xdd\x1fDqpk\"\u0357\x13R\xdak\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d46\x9e\xf7a\x19_:7>$\xec\xe6\xcd\"R\x0f\xe0\xb9\xe8n\x89\x1c\xff\xaf\xc9M\xb2\b\x80\x00\u07d46\xa0\x8f\xd6\xfd\x1a\xc1|\xe1^\xd5~\xef\xb1*+\u2048\xbf\x89Hz\x9a0E9D\x00\x00\u07d46\xa0\xe6\x1e\x1b\xe4\u007f\xa8~0\xd3(\x88\xee\x030\x90\x1c\xa9\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x946\xb2\xc8^:\xee\xeb\xb7\rc\u0124s\f\xe2\xe8\xe8\x8a6$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x946\xbfC\xff5\u07d0\x90\x88$3l\x9b1\xce3\x06~/P\x8aIr\x15\x10\xc1\xc1\xe9H\x00\x00\u07d46\xbf\xe1\xfa;{p\xc1r\xeb\x04/h\x19\xa8\x97%\x95A>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x946\xc5\x10\xbf\x8dnV\x9b\xf2\xf3}G&]\xbc\xb5\x02\xff+\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x946\xd8]\xc3h1V\xe6;\xf8\x80\xa9\xfa\xb7x\x8c\xf8\x14:'\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d46\u07cf\x88<\x12s\xec\x8a\x17\x1fz3\xcf\xd6I\xb1\xfe`u\x89\fRHJ\xc4\x16\x89\x00\x00\xe0\x946\xe1Va\f\xd8\xffd\xe7\x80\u061d\x00T8\\\xa7gU\xaa\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d46\xfe\xc6,,B^!\x9b\x18D\x8a\xd7W\x00\x9d\x8cT\x02o\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d47\x00\xe3\x02t$\xd99\xdb\xde]B\xfbx\xf6\xc4\xdb\xec\x1a\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d47\x02\xe7\x04\xcc!at9\xadN\xa2zW\x14\xf2\xfd\xa1\xe92\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x035\fMo\xe374,\xdd\xc6[\xf1\xe28k\xf3\xf9\xb2\x89m\x81!\xa1\x94\xd1\x10\x00\x00\xe0\x947\b\xe5\x9d\xe6\xb4\x05P\x88x)\x02\xe0W\x9cr\x01\xa8\xbfP\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d47\x126~^U\xa9mZ\x19\x16\x8fn\xb2\xbc~\x99q\xf8i\x8965\u026d\xc5\u07a0\x00\x00\u07d47\x19Zc]\xccb\xf5jq\x80I\xd4~\x8f\x9f\x96\x83(\x91\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d47'4\x1f&\xc1 \x01\xe3x@^\xe3\x8b-\x84d\xecq@\x89lk\x93[\x8b\xbd@\x00\x00\u07d47.E:kb\x9f'g\x8c\u022e\xb5\xe5|\xe8^\xc0\xae\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d474\xcb\x18t\x91\xed\xe7\x13\xae[;-\x12(J\xf4k\x81\x01\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d477!n\xe9\x1f\x17w2\xfbX\xfa@\x97&r\a\xe2\xcfU\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d47<T~\f\xb5\xcec.\x1cZ\xd6aUr\f\x01\xc4\t\x95\x89\xfeT\xdc\xdc\xe6\xc5Z\x00\x00\u07d47l\xd7Ws\x83\xe9\x02\x95\x1b`\xa2\x01{\xa7\xea)\xe35v\x89lk\x93[\x8b\xbd@\x00\x00\u07d47\x8e\xa1\u070e\xdc\x19\xba\xe8&8\x02\x9e\xa8u,\xe9\x8b\xcf\u0349lk\x93[\x8b\xbd@\x00\x00\u07d47\x8f7$??\xf0\xbe\xf5\xe1\u0705\xebC\b\xd94\f)\xf9\x89lnY\xe6|xT\x00\x00\u07d47\x95\x9c \xb7\xe9\x93\x1dr\xf5\xa8\xae\x86\x9d\xaf\u076d;m\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d47\x9a\u007fuZ\x81\xa1~\xdb}\xaa\xa2\x8a\xfcf]\xfak\xe6:\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d47\x9cqf\x84\x9b\xc2J\x02\xd6S^-\xef\x13\xda\xee\xf8\xaa\x8d\x89\x05k\xc7^-c\x10\x00\x00\xe0\x947\xa0Z\u03b99\\\x865\xa3\x9a|]&j\xe6\x10\xd1\v\xf2\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d47\xa1\x04Q\xf3af\xcfd=\xd2\xdel\x1c\xbb\xa8\xa0\x11\u03e3\x89\x14\x99\x8f2\xacxp\x00\x00\u07d47\xa7\xa6\xffN\xa3\xd6\x0e\xc3\a\xcaQjH\xd3\x05;\xb7\x9c\xbb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x947\xabf\b:O\xa28H\xb8\x86\xf9\xe6my\xcd\xc1P\xccp\x8a\x12\xbe\"\xff\xb5\xec\x008\x00\x00\u07d47\xac)\xbd\xa9?I{\u012e\xaa\xb95E,C\x15\x104\x1e\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d47\xb8\xbe\xac{\x1c\xa3\x88)\xd6\x1a\xb5R\xc7f\xf4\x8a\x10\xc3/\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d47\xbb\xc4r\x12\xd8/\xcb^\xe0\x8fR%\xec\xc2\x04\x1a\xd2\xda}\x89\xb1\xcf$\xdd\u0431@\x00\x00\u07d47\u02c6\x8d,?\x95\xb2Wa\x1e\xb3JA\x88\u054bt\x98\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d47\u0640\xa1.\xe3\xbf#\xcc\\\xdbc\xb4\xaeEi\x1ft\xc87\x89lk\x93[\x8b\xbd@\x00\x00\u07d47\xe1i\xa98\b\xd8\x03V\x98\xf8\x15\xc7#V\x13\xc1\xe6Y\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d47\xea\u0693\xc4u\xde\xd2\xf7\xe1^w\x87\xd4\x00G\x0f\xa5 b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d47\xfa\xc1\xe6\xbc\x12.\x93m\xfb\x84\xde\fK\xefn\r`\xc2\u05c9lk\x93[\x8b\xbd@\x00\x00\u07d48\a\xef\xf4:\xa9|v\x91\n\x19u-\xd7\x15\xee\x01\x82\xd9N\x89\r\x90\x15oo\xc2\xfb\x00\x00\u07d48\x15\xb0t?\x94\xfc\x8c\xc8eO\xd9\u0557\xed}\x8bw\xc5~\x89(\t\xd4)\u0616u\x00\x00\xe0\x948\x1d\xb4\xc8F]\xf4F\xa4\xce\x15\xbf\x81\xd4~/\x17\u0240\xbf\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\u07d48 ,\\\xd7\a\x8dO\x88vs\xab\a\x10\x9a\u062d\xa8\x97 \x8965\u026d\xc5\u07a0\x00\x00\u07d48!\x86$\x93$,\n\ub139\r\xe0]%\f\x1eP\xc0t\x89\x11wlX\xe9F\xdc\x00\x00\xe0\x948%\x91\xe7!{C^\x8e\x88L\xdb\xf4\x15\xfe7zo\u278a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d48+\xa7m\xb4\x1bu`m\u050aH\xf0\x13~\x91t\xe01\xb6\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d481u~\xaeuW\u02ca7\xa4\xb1\x06D\xb6>M;<u\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x9483\x04\xddzW \xb2\x9c\x1a\x10\xf6\x03B!\x9fH\x03/\x80\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\xe0\x948:|\x89\x9e\xe1\x8b\xc2\x14\x96\x98p\xbct\x82\xf6\xd8\xf3W\x0e\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x948C\x0e\x93\x1d\x93\xbe\x01\xb4\xc3\xef\r\xc55\xf1\xe0\xa9a\x00c\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d48C\x9a\xaa$\xe3co:\x18\xe0 \xea\x1d\xa7\xe1E\x16\r\x86\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d48E\x8e\x06\x85W<\xb4\u048fS\t\x88)\x90Ep\x17\x92f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d48Gfp8\xf3;\x01\xc1\xccy]\x8d\xafTu\xef\xf5\xa0\u0509'{\x9b\xf4$lA\x00\x00\u07d48d;\xab\xea`\x111l\u01d7\u0670\x93\u0217\xa1{\xda\xe7\x89\x12 \xbbtE\u06a0\x00\x00\u07d48i_\xc7\xe16|\xeb\x16>\xbb\x057Q\xf9\xf6\x8d\xdb\a\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d48r\xf4\x8d\xc5\xe3\xf8\x17\xbck*\xd2\xd00\xfc^\x04q\x19=\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48~\xea\xfdk@\t\u07af\x8b\u0578Zr\x98:\x8d\xcc4\x87\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48\x81\xde\xfa\xe1\xc0{<\xe0Lx\xab\xe2k\f\u070ds\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d48\x83\xbe\xcc\b\xb9\xbeh\xad;\b6\xaa\u00f6 \xdc\x00\x17\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d48\x85\xfe\xe6q\a\xdc:<t\x1e\xe2\x90\u0249\x18\u0279\x93\x97\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\x87\x19,\u007fpP\x06\xb60\t\x12v\xb3\x9a\u0180D\x8dk\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d48\x89\x8b\xbbES\xe0\v\xbf\xd0\xcf&\x8b/\xc4d\xd1T\xad\u0549\x11X\xe4`\x91=\x00\x00\x00\u07d48\x8b\xdc\xda\xe7\x94\xfcD\b.fu\x014A\x18\xea\x96\u0356\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d48\x8c\x85\xa9\xb9 }\x81F\x03?\xe3\x81C\xf6\xd3KY\\G\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d48\x96\xadt5y\u04ce#\x02EM\x1f\xb6\xe2\xabi\xe0\x1b\xfd\x89e\xea=\xb7UF`\x00\x00\u07d48\xa3\xdc\xcf/\xcf\xe0\xc9\x1a&$\xbd\f\xbf\x88\xeeJ\al3\x89lk\x93[\x8b\xbd@\x00\x00\u07d48\xa7D\xef\xa6\xd5\xc2\x13}\xef\xef\x8e\xf9\x18{d\x9e\xee\x1cx\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d48\xacfN\xe8\xe0y^Bu\u02c5+\u02e6\xa4y\xad\x9c\x8d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xb2\x19q\x06\x123\x87\xa0\xd4\xde6\x841\xa8\xba\xcd\xda0\xe2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xb3\x96\\!\xfa\x8991\a\x9b\xea\xcf\xff\xaf\x156x\xb6\xeb\x89\t<j\nQ\xe2g\x00\x00\u07d48\xb4\x03\xfb\x1f\xb7\xc1EY\xa2\xd6\xf6VJUR\xbc\xa3\x9a\xff\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x948\xb5\x01F\xe7\x19\x16\xa5D\x8d\xe1*Mt!5\xdc\xf3\x983\x8a\x06\u0450\xc4u\x16\x9a \x00\x00\u07d48\xbf*\x1fzi\xde\x0e%F\xad\xb8\b\xb3c5d]\xa9\xff\x89lp\x049\u0675`\x00\x00\xe0\x948\xc1\v\x90\xc8Y\u02f7\x81V\x92\xf9\x9d\xaeR\n\xb5\xfe\xbf^\x8a\x02\xc9\xe4\x96o\xa5\xcf$\x00\x00\u07d48\u01c5\x1f_\xfdL\xee\x98\xdf0\xf3\xb2U\x97\xaf\x8al\xa2c\x89\x8e\xad:/}~\x18\x00\x00\u07d48\xd2\xe9\x15Id\xb4\x1c\x8dP\xa7H}9\x1e~\xe2\xc3\xd3\u0089\xbd\xbcA\xe04\x8b0\x00\x00\xe0\x948\xda\x1b\xa2\u079e,\x95K\t-\xd9\xd8\x12\x04\xfd\x01k\xa0\x16\x8a\x02&\x8e\xd0\x1f4\xb30\x00\x00\u07d48\xdf\fJ\xbe}\xed_\xe0h\xea\xdf\x15J\u0191wC$\xa4\x89a\t=|,m8\x00\x00\u07d48\xe2\xafs9>\xa9\x8a\x1d\x99:t\xdf\\\xd7T\xb9\x8dR\x9a\x89a\t=|,m8\x00\x00\u07d48\xe4m\xe4E<8\xe9A\xe7\x93\x0fC0O\x94\xbb{+\xe8\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d48\xe7\u06e8\xfdO\x1f\x85\r\xbc&I\xd8\xe8O\tR\xe3\xeb<\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d48\xe8\xa3\x1a\xf2\xd2e\xe3\x1a\x9f\xff-\x8fF(m\x12E\xa4g\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d48\xeao[Z{\x88AuQ\xb4\x12=\xc1'\xdf\xe94-\xa6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d48\xee\xc6\xe2\x17\xf4\xd4\x1a\xa9 \xe4$\xb9RQ\x97\x04\x1c\xd4\u0189\xf0\r%\xeb\x92.g\x00\x00\xe0\x948\xf3\x87\xe1\xa4\xedJs\x10n\xf2\xb4b\xe4t\xe2\xe3\x14:\u040a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\x11a\xb0\xe4<0 f\u898d,\xe7\xe1\x99\xec\xdb\x1dW\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x949\x15\uad6b.Yw\xd0u\xde\xc4}\x96\xb6\x8bK\\\xf5\x15\x8a\r\a\x01\x81\x85\x12\x0f@\x00\x00\u07d49\x1aw@\\\t\xa7+^\x846#z\xaa\xf9]h\xda\x17\t\x89\x02\xa9&J\xf3\u0479\x00\x00\u07d49\x1f \x17m\x126\rrMQG\n\x90p6uYJM\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d49$3\xd2\u0383\xd3\xfbJv\x02\u0323\xfa\xcaN\xc1@\xa4\xb0\x89\x02\xc3\xc4e\xcaX\xec\x00\x00\xe0\x949?x;\\\u06c6\"\x1b\xf0)O\xb7\x14\x95\x9c{E\x89\x9c\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d49?\xf4%^\\e\x8f.\u007f\x10\xec\xbd)%rg\x1b\xc2\u0489lk\x93[\x8b\xbd@\x00\x00\u07d49A2`\x0fAU\xe0\u007fME\xbc>\xb8\xd9\xfbr\xdc\u05c4\x89\x9fn\x92\xed\xea\a\xd4\x00\x00\u07d49Q\xe4\x8e<\x86\x9ekr\xa1C\xb6\xa4Ph\u0379\xd4f\u0409\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x949T\xbd\xfe\v\xf5\x87\u0195\xa3\x05\xd9$L=[\xdd\xda\u027b\x8a\x04\x10'\x83'\xf9\x85`\x80\x00\u07d49]m%U \xa8\xdb)\xab\xc4}\x83\xa5\u06ca\x1a}\xf0\x87\x89\x05k\xc7^-c\x10\x00\x00\u07d49ck%\x81\x1b\x17j\xbf\xcf\xee\xcad\xbc\x87E/\x1f\xdf\xf4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d49i\xb4\xf7\x1b\xb8u\x1e\xdeC\xc0\x166:zaOv\x11\x8e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x949x/\xfe\x06\xacx\x82*<:\x8a\xfe0^P\xa5a\x88\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d49zn\xf8v:\x18\xf0\x0f\xac!~\x05\\\r0\x94\x10\x10\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d49|\u06cc\x80\xc6yP\xb1\x8deB)a\x0e\x93\xbf\xa6\xee\x1a\x89?\x95\xc8\xe0\x82\x15!\x00\x00\u07d49\x82O\x8b\xce\xd1v\xfd>\xa2.\u01a4\x93\xd0\xcc\xc3?\xc1G\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\x93l'\x19E\v\x94 \xcc%\"\u03d1\xdb\x01\xf2'\xc1\xc1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d49\x95\xe0\x96\xb0\x8aZrh\x00\xfc\xd1}\x9cd\xc6N\b\x8d+\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\x9a\xa6\xf5\xd0x\xcb\tp\x88+\u0259 \x06\xf8\xfb\xdf4q\x8965\u026d\xc5\u07a0\x00\x00\u07d49\xaa\x05\xe5m}28T!\u03d36\xe9\r=\x15\xa9\xf8Y\x89\x01h\u048e?\x00(\x00\x00\u07d49\xaa\xf0\x85M\xb6\xeb9\xbc{.C\x84jv\x17\x1c\x04E\u0789dI\xe8NG\xa8\xa8\x00\x00\u07d49\xb1\xc4q\xae\x94\xe1!dE.\x81\x1f\xbb\xe2\xb3\xcdru\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xb2\x992t\x90\xd7/\x9a\x9e\xdf\xf1\x1b\x83\xaf\xd0\xe9\xd3\xc4P\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d49\xba\u018d\x94xY\xf5\x9e\x92&\b\x9c\x96\xd6.\x9f\xbe<\u0789\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x949\xbf\xd9xh\x9b\xec\x04\x8f\xc7v\xaa\x15$\u007f^\x1d|9\xa2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d49\xc7s6|\x88%\xd3YlhoB\xbf\r\x141\x9e?\x84\x89\a?u\u0460\x85\xba\x00\x00\u07d49\u05291@,\fy\xc4W\x18o$\u07c7)\u03d5p1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d49\xd6\xca\xca\"\xbc\xcdjr\xf8~\xe7\u05b5\x9e\v\xde!\xd7\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d49\xe0\xdbM`V\x8c\x80\v\x8cU\x00\x02l%\x94\xf5v\x89`\x8965\u026d\xc5\u07a0\x00\x00\xe0\x949\xeeO\xe0\x0f\xbc\xeddph\xd4\xf5|\x01\xcb\"\xa8\v\xcc\u044a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d49\xf1\x983\x1eK!\xc1\xb7`\xa3\x15_J\xb2\xfe\x00\xa7F\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d49\xf4Fc\xd9%a\t\x1b\x82\xa7\r\xcfY=u@\x05\x97:\x89\n\u05cb.\xdc!Y\x80\x00\u07d4:\x03U\x94\xc7GGmB\xd1\xee\x96l6\"L\xdd\"I\x93\x89\x13J\xf7Ei\xf9\xc5\x00\x00\u07d4:\x04W(G\xd3\x1e\x81\xf7v\\\xa5\xbf\xc9\xd5W\x15\x9f6\x83\x89\a6-\r\xab\xea\xfd\x80\x00\xe0\x94:\x06\xe3\xbb\x1e\xdc\xfd\fD\xc3\aM\xe0\xbb`k\x04\x98\x94\xa2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794:\x10\x88\x8b~\x14\x9c\xae',\x010,2}\n\xf0\x1a\v$\x88\xeb\xec!\xee\x1d\xa4\x00\x00\u07d4:1\b\xc1\u6023;3l!\x13\x134@\x9d\x97\xe5\xad\xec\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4:6\x8e\xfeJ\u05c6\xe2c\x95\xec\x9f\u01adi\x8c\xae)\xfe\x01\x89\"E\x89\x96u\xf9\xf4\x00\x00\u07d4:=\xd1\x04\xcd~\xb0O!\x93/\xd43\xeaz\xff\u04d3i\xf5\x89\x13aO#\xe2B&\x00\x00\u07d4:B\x97\xda<U^F\xc0sf\x9d\x04x\xfc\xe7_/y\x0e\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4:Gk\xd2\xc9\xe6d\xc6:\xb2f\xaaLnJH%\xf5\x16\u00c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4:H\xe0\xa7\t\x8b\x06\xa9\x05\x80+\x87TW1\x11\x8e\x89\xf49\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:M\xa7\x8d\xce\x05\xae\xb8}\u9bad\x91\x85rm\xa1\x92g\x98\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4:Y\xa0\x82F\xa8 o\x8dX\xf7\v\xb1\xf0\xd3\\[\xccq\xbd\x89\n\ad\a\xd3\xf7D\x00\x00\xe0\x94:r\xd65\xaa\xde\xeeC\x824\x9d\xb9\x8a\x18\x13\xa4\xcf\xeb=\xf1\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94:}\xb2$\xac\xae\x17\xdew\x98y}\x82\xcd\xf8%0\x17\u07e8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4:\x80_\xa0\xf78\u007fs\x05[xX\u0285\x19\xed\xd9=cO\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94:\x84\xe9P\xedA\x0eQ\xb7\xe8\x80\x10I\xab&4\xb2\x85\xfe\xa1\x8a\x03\xf5/\u06a8\"\xd2\xc8\x00\x00\u07d4:\x86\ue506+t=\xd3OA\ti\xd9N,VR\u052d\x89\n\xed\xe6\x9a\xd3\x0e\x81\x00\x00\u07d4:\x912\xb7\t=>\xc4.\x1eO\xb8\xcb1\xec\xddC\xaew<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94:\x99`&m\xf6I cS\x8a\x99\xf4\x87\xc9P\xa3\xa5\uc78a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4:\x9b\x11\x10)\xce\x1f \xc9\x10\x9czt\xee\xee\xf3OO.\xb2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4:\x9eTA\xd4K$;\xe5[u\x02z\x1c\ub7ac\xf5\r\xf2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94:\xa0z4\xa1\xaf\u0216}=\x13\x83\xb9kb\u03d6\xd5\xfa\x90\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94:\xa4,!\xb9\xb3\x1c>'\xcc\xd1~\t\x9a\xf6y\xcd\xf5i\a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4:\xa9H\xea\x029wU\xef\xfb/\x9d\xc99-\xf1\x05\x8f~3\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4:\xad\xf9\x8ba\xe5\u0216\xe7\xd1\x00\xa39\x1d2P\"]a\u07c9\f\xafg\x007\x01h\x00\x00\u07d4:\xaeHr\xfd\x90\x93\xcb\xca\xd1@o\x1e\x80x\xba\xb5\x03Y\xe2\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4:\xbb\x8a\xdf\xc6\x04\xf4\x8dY\x84\x81\x1d\u007f\x1dR\xfe\xf6u\x82p\x89\xf2\x97\x19\xb6o\x11\f\x00\x00\u07d4:\xc2\xf0\xff\x16\x12\xe4\xa1\xc3F\xd53\x82\xab\xf6\u0622[\xaaS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xc9\xdczCj\xe9\x8f\xd0\x1cz\x96!\xaa\x8e\x9d\v\x8bS\x1d\x89a\t=|,m8\x00\x00\xe0\x94:\xd0aI\xb2\x1cU\xff\x86|\xc3\xfb\x97@\u04bc\xc7\x10\x121\x8a)\xb7d2\xb9DQ \x00\x00\u07d4:\xd7\x02C\u060b\xf0@\x0fW\xc8\xc1\xfdW\x81\x18H\xaf\x16*\x89.\x9e\xe5\u00c6S\xf0\x00\x00\u07d4:\xd9\x15\xd5P\xb7#AV \xf5\xa9\xb5\xb8\x8a\x85\xf3\x82\xf05\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xe1`\xe3\xcd`\xae1\xb9\xd6t-h\xe1Nv\xbd\x96\xc5\x17\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4:\xe6+\xd2q\xa7`c\u007f\xady\xc3\x1c\x94\xffb\xb4\xcd\x12\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4:\xeaN\x82\xd2@\x02H\xf9\x98q\xa4\x1c\xa2W\x06\r:\"\x1b\x8965\u026d\xc5\u07a0\x00\x00\u07d4:\xf6[>(\x89ZJ\x00\x11S9\x1d\x1ei\xc3\x1f\xb9\xdb9\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4;\a\xdbZ5\u007fZ\xf2HL\xbc\x9dw\xd7;\x1f\xd0Q\x9f\u01c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4;\n\u032fK`|\xfea\xd1s4\xc2\x14\xb7\\\xde\xfd\xbd\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x13c\x1a\x1b\x89\xcbVeH\x89\x9a\x1d`\x91\\\xdc\xc4 [\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x15\x90\x99\aR\a\u0180vc\xb1\xf0\xf7\xed\xa5J\xc8\xcc\xe3\x89j\xc4\xe6[i\xf9-\x80\x00\u07d4;\x197\xd5\u74f8\x9bc\xfb\x8e\xb5\xf1\xb1\xc9\xcak\xa0\xfa\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\"\xda*\x02q\xc8\xef\xe1\x02S'scji\xb1\xc1~\t\x89\x1b6\xa6DJ>\x18\x00\x00\u07d4;\"\u07a3\xc2_\x1bY\u01fd'\xbb\x91\u04e3\xea\xec\xef9\x84\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94;#g\xf8IK_\xe1\x8dh<\x05]\x89\x99\x9c\x9f=\x1b4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;,E\x99\x0e!GDQ\xcfOY\xf0\x19U\xb31\xc7\xd7\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94;A\x00\xe3\ns\xb0\xc74\xb1\x8f\xfa\x84&\u045b\x191/\x1a\x8a\v\xb5\u046ap\n\xfd\x90\x00\x00\u07d4;B\xa6m\x97\x9fX(4tz\x8b`B\x8e\x9bN\xec\xcd#\x89!\xa1\u01d0\xfa\xdcX\x00\x00\u07d4;Gh\xfdq\xe2\xdb,\xbe\u007f\xa0PH<'\xb4\xeb\x93\x1d\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;Vj\x8a\xfa\u0456\x82\xdc,\xe8g\x9a<\xe4D\xa5\xb0\xfdO\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\\%\x1d\u007f\u05c9;\xa2\t\xfeT\x1c\xec\xd0\xce%:\x99\r\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4;^\x8b<w\xf7\x92\xde\xcbz\x89\x85\u07d1n\xfbI\n\xac#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;n\x81Ow\aH\xa7\u00d9x\x064v\x05H\n?\xd5\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;{OS\xc4VU\xf3\xdc_\x01~\xdc#\xb1o\x9b\xc56\xfa\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94;{\x8e'\xde3\xd3\xceya\xb9\x8d\x19\xa5/\xe7\x9fl%\xbe\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4;|w\xdb\xe9]\xc2`,\xe3&\x9a\x95E\xd0Ie\xfe\xfd\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\x80\x98S?}\x9b\xdc\xd3\a\u06f2>\x17w\xca\x18A\x896\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94;\x93\xb1a6\xf1\x1e\xaf\x10\x99l\x95\x99\r;'9\xcc\xea_\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4;\xabK\x01\xa7\xc8K\xa1?\uea70\xbb\x19\x1bw\xa3\xaa\u0723\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4;\xb55\x98\xcc \xe2\x05]\xc5S\xb0I@J\u0277\xdd\x1e\x83\x89!W\x1d\xf7|\x00\xbe\x00\x00\u07d4;\xbc\x13\xd0J\xcc\xc0pz\xeb\u072e\xf0\x87\u0438~\v^\u327e\xd1\xd0&=\x9f\x00\x00\x00\u07d4;\xc6\xe3\xeezV\u038f\x14\xa3u2Y\x0fcqk\x99f\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\xc8]ls[\x9c\xdaK\xba_H\xb2K\x13\xe7\x0600{\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4;\xd6$\xb5H\xcbe\x976\x90~\u062a<\fp^$\xb5u\x89lk\x93[\x8b\xbd@\x00\x00\u07d4;\u0660m\x1b\xd3lN\xdd'\xfc\r\x1f[\b\x8d\xda\xe3\xc7*\x89\x1b\x1azB\v\xa0\r\x00\x00\u0794;\u077c\x814\xf7}UY\u007f\xc9|&\xd2f\x98\t\x06\x04\ub23e -j\x0e\xda\x00\x00\xe0\x94;\xf8n\u0623\x15>\xc93xj\x02\xac\t\x03\x01\x85^Wk\x8a_J\x8c\x83u\xd1U@\x00\x00\u07d4;\xfb\u04c4|\x17\xa6\x1c\xf3\xf1{R\xf8\ub879`\xb3\U000df262\xa1]\tQ\x9b\xe0\x00\x00\u07d4<\x03\xbb\xc0#\xe1\xe9?\xa3\xa3\xa6\xe4(\xcf\f\xd8\xf9^\x1e\u0189Rf<\u02b1\xe1\xc0\x00\x00\u07d4<\f=\ufb1c\xeaz\xcc1\x9a\x96\xc3\v\x8e\x1f\xed\xabEt\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4<\x15\xb3Q\x1d\xf6\xf04.sH\u0309\xaf9\xa1h\xb7s\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x1f\x91\xf3\x01\xf4\xb5e\xbc\xa2GQ\xaa\x1fv\x13\"p\x9d\u0749a\t=|,m8\x00\x00\xe0\x94<(l\xfb0\x14n_\u05d0\xc2\xc8T\x15RW\x8d\xe34\u060a\x02)\x1b\x11\xaa0n\x8c\x00\x00\u07d4<2.a\x1f\u06c2\rG\xc6\xf8\xfcd\xb6\xfa\xd7L\xa9_^\x89\r%\x8e\xce\x1b\x13\x15\x00\x00\u07d4<Z$\x14Y\u01ab\xbfc\x029\u024a0\xd2\v\x8b:\xc5a\x89\b\x8b#\xac\xff\u0650\x00\x00\u07d4<y\xc8c\xc3\xd3r\xb3\xff\foE'4\xa7\xf9pB\xd7\x06\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\xe0\x94<\x83\xc1p\x1d\xb08\x8bh!\r\x00\xf5q|\u067d2,j\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4<\x86\x0e.f?F\xdbSB{)\xfe>\xa5\xe5\xbfb\xbb\u0309\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4<\x86\x9c\tie#\xce\xd8$\xa0pAF\x05\xbbv#\x1f\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4<\x92V\x19\u02731DF?\x057\u06165\x87\x06\xc5 \xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\x98YK\xf6\x8bW5\x1e\x88\x14\xae\x9em\xfd-%J\xa0o\x89\x10CV\x1a\x88)0\x00\x00\u07d4<\xad\xeb=>\xed?b1\x1dRU>p\xdfJ\xfc\xe5o#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94<\xae\xdbS\x19\xfe\x80eC\xc5nP!\xd3r\xf7\x1b\xe9\x06.\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4<\xaf\xaf^bPV\x15\x06\x8a\xf8\xeb\"\xa1:\u0629\xe5Pp\x89lf\x06E\xaaG\x18\x00\x00\u07d4<\xb1y\xcbH\x01\xa9\x9b\x95\u00f0\xc3$\xa2\xbd\xc1\x01\xa6S`\x89\x01h\u048e?\x00(\x00\x00\u07d4<\xb5a\u0386BK5\x98\x91\xe3d\xec\x92_\xfe\xff'}\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xcbq\xaah\x80\xcb\v\x84\x01-\x90\xe6\a@\xec\x06\xac\u05cf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\xce\xf8\x86yW9G\xe9I\x97y\x8a\x1e2~\b`:e\x89+\xc9\x16\u059f;\x02\x00\x00\xe0\x94<\xd1\xd9s\x1b\xd5H\xc1\xddo\u03a6\x1b\xebu\xd9\x17T\xf7\u04ca\x01\x16\x1d\x01\xb2\x15\xca\xe4\x80\x00\u07d4<\u04e6\xe95y\xc5mIAq\xfcS>z\x90\xe6\xf5\x94d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4<\u05b7Y<\xbe\xe7x0\xa8\xb1\x9d\b\x01\x95\x8f\xcdK\xc5z\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4<\xd7\xf7\xc7\xc257\x80\xcd\xe0\x81\xee\xecE\x82+%\xf2\x86\f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xe1\u0717\xfc\u05f7\xc4\u04e1\x8aI\xd6\xf2\xa5\xc1\xb1\xa9\x06\u05c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4<\xea0*G*\x94\x03y\xdd9\x8a$\xea\xfd\xba\u07c8\xady\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94<\xec\xa9k\xb1\xcd\xc2\x14\x02\x9c\xbc^\x18\x1d9\x8a\xb9M=A\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4<\xf4\x84RO\xbd\xfa\xda\xe2m\xc1\x85\xe3++c\x0f\xd2\xe7&\x89\x18TR\xcb*\x91\xc3\x00\x00\u07d4<\xf9\xa1\xd4e\xe7\x8bp9\xe3iDx\xe2b{6\xfc\xd1A\x89J`S*\xd5\x1b\xf0\x00\x00\u07d4<\xfb\xf0fVYpc\x9e\x13\r\xf2\xa7\xd1k\x0e\x14\xd6\t\x1c\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94=\th\x8d\x93\xad\a\xf3\xab\xe6\x8cr'#\xcdh\t\x90C^\x8a\x06ZL\xe9\x9fv\x9en\x00\x00\u07d4=1X{_\u0546\x98Ex\x87%\xa6c)\nI\xd3g\x8c\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4=?\xadI\xc9\xe5\xd2u\x9c\x8e\x8eZzM`\xa0\xdd\x13V\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=WO\xcf\x00\xfa\xe1\u064c\u023f\x9d\u07e1\xb3\x95;\x97A\xbc\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4=Z\x8b+\x80\xbe\x8b5\xd8\xec\xf7\x89\xb5\xedz\au\xc5\al\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4=f\xcdK\xd6M\\\x8c\x1b^\xea(\x1e\x10m\x1cZ\xad#s\x89i\xc4\xf3\xa8\xa1\x10\xa6\x00\x00\u0794=j\xe0S\xfc\xbc1\x8do\xd0\xfb\xc3S\xb8\xbfT.h\r'\x88\xc6s\xce<@\x16\x00\x00\u07d4=o\xf8,\x93w\x05\x9f\xb3\r\x92\x15r?`\xc7u\u0211\xfe\x89\r\x8e\\\xe6\x17\xf2\xd5\x00\x00\u07d4=y\xa8S\xd7\x1b\xe0b\x1bD\xe2\x97Yel\xa0u\xfd\xf4\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=~\xa5\xbf\x03R\x81\x00\xed\x8a\xf8\xae\xd2e>\x92\x1bng%\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\x81?\xf2\xb6\xedW\xb97\u06bf+8\x1d\x14\x8aA\x1f\xa0\x85\x89\x05k\xc7^-c\x10\x00\x00\u07d4=\x88\x143\xf0J}\r'\xf8ID\xe0\x8aQ-\xa3UR\x87\x89A\rXj \xa4\xc0\x00\x00\u07d4=\x89\xe5\x05\xcbF\xe2\x11\xa5?2\xf1g\xa8w\xbe\xc8\u007fK\n\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94=\x8d\a#r\x1es\xa6\xc0\xd8`\xaa\x05W\xab\xd1L\x1e\xe3b\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4=\x8f9\x88\x1b\x9e\xdf\xe9\x12'\xc3?\xa4\xcd\xd9\x1eg\x85D\xb0\x89\x04\xab\a\xbaC\xad\xa9\x80\x00\u07d4=\x9dk\xe5\u007f\xf8>\x06Y\x85fO\x12VD\x83\xf2\xe6\x00\xb2\x89n\xac\xe4?#\xbd\x80\x00\x00\u07d4=\xa3\x9c\xe3\xefJz9f\xb3.\xe7\xeaN\xbc#5\xa8\xf1\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4=\xaa\x01\u03b7\x0e\xaf\x95\x91\xfaR\x1b\xa4\xa2~\xa9\xfb\x8e\xdeJ\x89Zc\xd2\u027cvT\x00\x00\u07d4=\xb5\xfejh\xbd6\x12\xac\x15\xa9\x9aa\xe5U\x92\x8e\xec\xea\xf3\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4=\xb9\xed\u007f\x02L~&7/\xea\xcf+\x05\b\x03D^8\x10\x89E\xb1H\xb4\x99j0\x00\x00\u07d4=\xbf\r\xbf\xd7x\x90\x80\x053\xf0\x9d\xea\x83\x01\xb9\xf0%\u04a6\x8965\u026d\xc5\u07a0\x00\x00\u07d4=\xce\U0005c18b\x15\xd3N\xdaBn\xc7\xe0K\x18\xb6\x01p\x02\x89lh\xcc\u041b\x02,\x00\x00\xe0\x94=\xd1.Uj`76\xfe\xbaJo\xa8\xbdJ\xc4]f*\x04\x8a#u{\x91\x83\xe0x(\x00\x00\u07d4=\u078b\x15\xb3\u033a\xa5x\x01\x12\xc3\xd6t\xf3\x13\xbb\xa6\x80&\x89`\x1dQZ>O\x94\x00\x00\xe0\x94=\xde\xdb\xe4\x89#\xfb\xf9\xe56\xbf\x9f\xfb\aG\xc9\xcd\u04de\xef\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4=\xea\xe43'\x91?b\x80\x8f\xaa\x1bbv\xa2\xbdch\xea\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4=\xf7b\x04\x9e\u068a\u0192}\x90Lz\xf4/\x94\xe5Q\x96\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\x04\r@\u02c0\xba\x01%\xf3\xb1_\xde\xfc\xc8?0\x05\xda\x1b\x898E$\xccp\xb7x\x00\x00\u07d4>\v\x8e\xd8n\xd6i\xe1'#\xafur\xfb\xac\xfe\x82\x9b\x1e\x16\x89QM\xe7\xf9\xb8\x12\xdc\x00\x00\xe0\x94>\f\xbejm\xcba\xf1\x10\xc4[\xa2\xaa6\x1d\u007f\xca\xd3\xdas\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4>\x19KN\xce\xf8\xbbq\x1e\xa2\xff$\xfe\xc4\xe8{\xd02\xf7\u0449\x8b\x9d\xc1\xbc\x1a\x03j\x80\x00\xe0\x94>\x1b\"0\xaf\xbb\xd3\x10\xb4\x92jLwmZ\u705cf\x1d\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4>\x1cS0\x0eL\x16\x89\x12\x16<~\x99\xb9]\xa2h\xad(\n\x896b2\\\u044f\xe0\x00\x00\u07d4>\x1c\x96 c\xe0\xd5)YA\xf2\x10\u0723\xabS\x1e\xec\x88\t\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4>,\xa0\xd24\xba\xf6\a\xadFj\x1b\x85\xf4\xa6H\x8e\xf0\n\xe7\x89\x04\xda!\xa3H=V\x80\x00\u07d4>/&#^\x13zs$\xe4\xdc\x15K]\xf5\xafF\xea\x1aI\x89\x017\xaa\xd8\x03-\xb9\x00\x00\xe0\x94>1a\xf1\xea/\xbf\x12ny\xda\x18\x01\u0695\x12\xb3y\x88\u024a\nm\xd9\f\xaeQ\x14H\x00\x00\xe0\x94>6\xc1rS\xc1\x1c\xf3\x89t\xed\r\xb1\xb7Y\x16\r\xa67\x83\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4><\u04fe\xc0e\x91\xd64o%Kb\x1e\xb4\x1c\x89\x00\x8d1\x895\u07fe\u069f74\x00\x00\u07d4>E\xbdU\u06d0`\xec\xed\x92;\xb9\xcbs<\xb3W?\xb51\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4>M\x13\xc5Z\x84\xe4n\xd7\xe9\u02d0\xfd5^\x8a\u0651\u33c965\u026d\xc5\u07a0\x00\x00\u07d4>N\x92e\"<\x9782L\xf2\v\xd0`\x06\xd0\a>\u06cc\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94>O\xbdf\x10\x15\xf6F\x1e\xd6s\\\xef\xef\x01\xf3\x14E\xde:\x8a\x03n4)\x98\xb8\xb0 \x00\x00\xe0\x94>S\xff!\a\xa8\u07be3(I:\x92\xa5\x86\xa7\xe1\xf4\x97X\x8a\x04\xe6\x9c*q\xa4\x05\xab\x00\x00\u07d4>Z9\xfd\xdap\xdf\x11&\xab\r\u011asx1\x1aSz\x1f\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\xe0\x94>Z\xbd\t\xceZ\xf7\xba\x84\x87\xc3Y\xe0\xf2\xa9:\x98k\v\x18\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4>\\\xb8\x92\x8cAx%\xc0:;\xfc\xc5!\x83\xe5\xc9\x1eB\u05c9\xe71\xd9\xc5,\x96/\x00\x00\u07d4>^\x93\xfbL\x9c\x9d\x12F\xf8\xf2G5\x8e\"\xc3\xc5\xd1{j\x89\b!\xab\rD\x14\x98\x00\x00\u07d4>a\x83P\xfa\x01ez\xb0\xef>\xba\xc8\xe3p\x12\xf8\xfc+o\x89\x98\x06\xde=\xa6\xe9x\x00\x00\u07d4>c\xce;$\xca(e\xb4\u0166\x87\xb7\xae\xa3Y~\xf6\xe5H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>f\xb8GiVj\xb6yE\xd5\xfa\x8175V\xbc\u00e1\xfa\x89\b=lz\xabc`\x00\x00\xe0\x94>v\xa6-\xb1\x87\xaat\xf68\x17S;0l\xea\xd0\xe8\u03be\x8a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4>z\x96k]\xc3W\xff\xb0~\x9f\xe0g\xc4W\x91\xfd\x8e0I\x89\x034-`\xdf\xf1\x96\x00\x00\xe0\x94>\x81w!u#~\xb4\xcb\xe0\xfe-\xca\xfd\xad\xff\xebj\x19\x99\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4>\x83I\xb6\u007fWED\x9fe\x93g\u066dG\x12\xdb[\x89Z\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4>\x83TO\x00\x82U%r\u01c2\xbe\xe5\xd2\x18\xf1\xef\x06J\x9d\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4>\x84\xb3\\[\"ePpa\xd3\vo\x12\xda\x03?\xe6\xf8\xb9\x89a\t=|,m8\x00\x00\u07d4>\x86A\xd4<B\x00?\n3\xc9)\xf7\x11\a\x9d\xeb+\x9eF\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94>\x87E\xba2/_\xd6\xcbP\x12N\xc4f\x88\u01e6\x9a\u007f\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4>\x91N0\x18\xac\x00D\x93A\u011d\xa7\x1d\x04\xdf\xee\xedb!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4>\x94\x10\u04f9\xa8~\xd5\xe4Q\xa6\xb9\x1b\xb8\x92?\xe9\x0f\xb2\xb5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4>\x94\xdfS\x13\xfaR\x05p\xef#+\xc31\x1d_b/\xf1\x83\x89lk\x93[\x8b\xbd@\x00\x00\u0794>\x9b4\xa5\u007f3u\xaeY\xc0\xa7^\x19\u0136A\"\x8d\x97\x00\x88\xf8i\x93)g~\x00\x00\u07d4>\xad\xa8\xc9/V\x06~\x1b\xb7<\xe3x\xdaV\xdc,\xdf\xd3e\x89w\xcd\xe9:\xeb\rH\x00\x00\xe0\x94>\xaf\by\xb5\xb6\xdb\x15\x9bX\x9f\x84W\x8bjt\xf6\xc1\x03W\x8a\x01\x898\xb6q\xfae\xa2\x80\x00\u07d4>\xaf1k\x87a]\x88\xf7\xad\xc7|X\xe7\x12\xedMw\x96k\x89\x05m\xbcL\xee$d\x80\x00\u07d4>\xb8\xb3;!\xd2<\u0686\xd8(\x88\x84\xabG\x0e\x16F\x91\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4>\xb9\xef\x06\xd0\xc2Y\x04\x03\x19\x94~\x8czh\x12\xaa\x02S\u0609\t\r\x97/22<\x00\x00\u07d4>\u030e\x16h\xdd\xe9\x95\xdcW\x0f\xe4\x14\xf4B\x11\xc54\xa6\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4>\u03752\xe3\x97W\x96b\xb2\xa4aA\u73c25\x93j_\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4>\xeeo\x1e\x966\vv\x89\xb3\x06\x9a\xda\xf9\xaf\x8e\xb6\f\u404965\u026d\xc5\u07a0\x00\x00\xe0\x94?\b\u066d\x89O\x81>\x8e!H\xc1`\xd2K5:\x8et\xb0\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4?\f\x83\xaa\xc5qybsN\\\xea\xea\xec\u04db(\xad\x06\xbe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94?\x10\x80\x02\x82\u0477\xdd\u01cf\xa9-\x820\aN\x1b\xf6\xae\xae\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4?\x123qO M\xe9\xdeN\xe9m\a;6\x8d\x81\x97\x98\x9f\x89\x02\x17\xc4\x10t\xe6\xbb\x00\x00\u07d4?\x17:\xa6\xed\xf4i\u0445\xe5\x9b\xd2j\xe4#k\x92\xb4\xd8\xe1\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4?\x1b\xc4 \xc5<\x00,\x9e\x90\x03|D\xfej\x8e\xf4\xdd\xc9b\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4?#a\b\xee\xc7\"\x89\xba\u00e6\\\u0483\xf9^\x04\x1d\x14L\x8964\xbf9\xab\x98x\x80\x00\u07d4?-\xa0\x93\xbb\x16\xeb\x06O\x8b\xfa\x9e0\xb9)\xd1_\x8e\x1cL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?-\xd5]\xb7\xea\xb0\xeb\xeee\xb3>\xd8 ,\x1e\x99.\x95\x8b\x89,s\xc97t,P\x00\x00\u07d4?/8\x14\x91y|\xc5\xc0\u0502\x96\xc1O\xd0\xcd\x00\xcd\xfa-\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4?0\u04fc\x9f`\"2\xbcrB\x88\xcaF\xcd\v\a\x88\xf7\x15\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4?<\x8ea\xe5`L\xef\x06\x05\xd46\xdd\"\xac\u0346\"\x17\xfc\x89Hz\x9a0E9D\x00\x00\u07d4??F\xb7\\\xab\xe3{\xfa\u0307`(\x1fCA\xca\u007fF=\x89 \xacD\x825\xfa\xe8\x80\x00\u07d4?G)c\x19x\x83\xbb\xdaZ\x9b}\xfc\xb2-\xb1\x14@\xad1\x89\x1a\x19d<\xb1\xef\xf0\x80\x00\u07d4?L\xd19\x9f\x8a4\xed\u06da\x17\xa4q\xfc\x92+Xp\xaa\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?U\x1b\xa9<\xd5F\x93\xc1\x83\xfb\x9a\xd6\re\xe1`\x96s\u0249lk\x93[\x8b\xbd@\x00\x00\xe0\x94?bzv\x9ej\x95\x0e\xb8p\x17\xa7\u035c\xa2\bq\x13h1\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4?m\xd3e\x0e\xe4(\u0737u\x95S\xb0\x17\xa9j\x94(j\u0249Hz\x9a0E9D\x00\x00\u07d4?tr7\x80o\xed?\x82\x8ahR\xeb\bg\xf7\x90'\xaf\x89\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4?u\xaea\xcc\x1d\x80Be;[\xae\xc4D>\x05\x1c^z\xbd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4?\xb7\u0457\xb3\xbaO\xe0E\xef\xc2=P\xa1E\x85\xf5X\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94?\xbc\x1eE\x18\xd74\x00\xc6\xd0F5\x949\xfbh\xea\x1aI\xf4\x8a\x03y\v\xb8U\x13v@\x00\x00\u07d4?\xbe\xd6\xe7\xe0\u029c\x84\xfb\xe9\xeb\u03ddN\xf9\xbbIB\x81e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\u043bGy\x8c\xf4L\u07feM3=\xe67\xdfJ\x00\xe4\\\x89\x05lUy\xf7\"\x14\x00\x00\xe0\x94?\xe4\x0f\xbd\x91\x9a\xad(\x18\xdf\x01\xeeM\xf4lF\x84*\xc59\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4?\xe8\x01\xe6\x135\xc5\x14\r\xc7\xed\xa2\xefR\x04F\nP\x120\x89lk\x93[\x8b\xbd@\x00\x00\u07d4?\xf86\xb6\xf5{\x90\x1bD\f0\xe4\xdb\xd0e\xcf7\xd3\u050c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4?\xfc\xb8p\xd4\x02=%]Qg\u0625\a\xce\xfc6kh\xba\x89#4<CT\u04ac\x00\x00\u0794@\x13T\xa2\x97\x95/\xa9r\xad8<\xa0z\n(\x11\xd7Jq\x88\xc2I\xfd\xd3'x\x00\x00\u07d4@0\xa9%pk,\x10\x1c\x8c\\\xb9\xbd\x05\xfb\xb4\xf6u\x9b\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94@1E\xcbJ\xe7H\x9f\u0310\u0358\\m\u01c2\xb3\xccND\x8a\x01E?\xf3\x87\xb2|\xac\x00\x00\u07d4@2 `\n6\xf7?$\xe1\x90\xd1\xed\xb2\xd6\x1b\xe3\xf4\x13T\x89\x10z\xd8\xf5V\xc6\xc0\x00\x00\u07d4@9\xbdP\xa2\xbd\xe1_\xfe7\x19\x1fA\x03\x90\x96*+\x88\x86\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4@<d\x89ju\xca\xd8\x16\xa9\x10^\x18\u062a[\xf8\x0f#\x8e\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94@=S\xcfb\x0f\t\"\xb4\x17\x84\x8d\xee\x96\xc1\x90\xb5\xbc\x82q\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4@A\x00\xdbL]\x0e\xecUx#\xb5\x83Cu\x8b\xcc,\x80\x83\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4@A7K\x0f\xee\xf4y.K3i\x1f\xb8h\x97\xa4\xffV\f\x89\x13\xc9d~%\xa9\x94\x00\x00\u07d4@F}\x80\xe7L5@{}\xb5\x17\x89#F\x15\xfe\xa6h\x18\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4@XR\x00h:@9\x017)\x12\xa8\x984\xaa\u0735_\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4@X\x80\x88\x16\xfd\xaa:_\u024e\xd4|\xfa\xe6\xc1\x83\x15B.\x89\n\xd4\xc81j\v\f\x00\x00\u07d4@_Yk\x94\xb9G4L\x03<\xe2\u073f\xf1.%\xb7\x97\x84\x89lk\x93[\x8b\xbd@\x00\x00\u07d4@c\x00$\xbd,X\xd2H\xed\xd8FV\x17\xb2\xbf\x16G\xda\x0e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94@e#`\xd6qm\xc5\\\xf9\xaa\xb2\x1f4\x82\xf8\x16\xcc,\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94@r\x95\xeb\xd9KH&\x9c-V\x9c\x9b\x9a\xf9\xaa\x05\xe8>^\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4@s\xfaI\xb8q\x17\u02d0\x8c\xf1\xabQ-\xa7T\xa92\xd4w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4@\x8ai\xa4\a\x15\xe1\xb3\x13\xe15N`\b\x00\xa1\xe6\xdc\x02\xa5\x89\x01\u7e11\u0312T\x00\x00\u07d4@\x9b\xd7P\x85\x82\x1c\x1d\xe7\f\xdc;\x11\xff\xc3\xd9#\xc7@\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\x9dZ\x96.\xde\uefa1x\x01\x8c\x0f8\xb9\u0372\x13\xf2\x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4@\xa31\x19[\x97s%\u00aa(\xfa/B\xcb%\xec<%<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4@\xa7\xf7(g\xa7\u0706w\v\x16+uW\xa44\xedP\xcc\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xab\n>\x83\xd0\u022c\x93f\x91\x05 \xea\xb1w+\xac;\x1a\x894\xf1\f-\xc0^|\x00\x00\u07d4@\xabf\xfe!>\xa5l:\xfb\x12\xc7[\xe3?\x8e2\xfd\b]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94@\xadt\xbc\v\xce*E\xe5/6\xc3\u07bb\x1b:\xda\x1bv\x19\x8a\x01p\x16-\xe1\t\xc6X\x00\x00\u07d4@\u03c9\x05\x91\xea\u484f\x81*)T\xcb)_c3'\xe6\x89\x02\x9b\xf76\xfcY\x1a\x00\x00\u07d4@\u03d0\xef[v\x8c]\xa5\x85\x00,\xcb\xe6avP\xd8\xe87\x8963\x03\"\xd5#\x8c\x00\x00\xe0\x94@\xd4]\x9dv%\xd1QV\xc92\xb7q\xca{\x05'\x13\tX\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4@\xdb\x1b\xa5\x85\xce4S\x1e\xde\xc5IHI9\x13\x81\xe6\xcc\u04c9a\t=|,m8\x00\x00\xe0\x94@\xdfI^\xcf?\x8bL\xef*l\x18\x99W$\x8f\u813c+\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4@\xe0\xdb\xf3\xef\uf404\xea\x1c\xd7\xe5\x03\xf4\v;J\x84C\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4@\xe2D\n\xe1B\u02006j\x12\xc6\xd4\x10/K\x844\xb6*\x8965\u026d\xc5\u07a0\x00\x00\u07d4@\xe3\u0083\xf7\xe2M\xe0A\f\x12\x1b\xee`\xa5`\u007f>)\xa6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94@\xeaPD\xb2\x04\xb20v\xb1\xa5\x80;\xf1\xd3\f\x0f\x88\x87\x1a\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94@\xed\xdbD\x8di\x0e\xd7.\x05\xc2%\xd3O\xc85\x0f\xa1\xe4\u014a\x01{x\x83\xc0i\x16`\x00\x00\xe0\x94@\xf4\xf4\xc0ls,\xd3[\x11\x9b\x89;\x12~}\x9d\aq\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\x01\x0f\u023a\xf8C}\x17\xa0Ci\x80\x9a\x16\x8a\x17\xcaV\xfb\x89\x05k\xc7^-c\x10\x00\x00\u07d4A\x03)\x96q\xd4gc\x97\x8f\xa4\xaa\x19\xee4\xb1\xfc\x95'\x84\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4A\x03<\x1bm\x05\xe1\u0289\xb0\x94\x8f\xc6DS\xfb\xe8z\xb2^\x89Hz\x9a0E9D\x00\x00\u07d4A\t\x8a\x81E#\x17\xc1\x9e>\xef\v\xd1#\xbb\xe1x\xe9\xe9\u0289\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4A\x16\x10\xb1x\xd5a}\xfa\xb94\u0493\xf5\x12\xa9>\\\x10\xe1\x89\t79SM(h\x00\x00\u07d4A\x1c\x83\x1c\xc6\xf4O\x19e\xecWW\xabN[<\xa4\xcf\xfd\x1f\x89\x17\n\x0fP@\xe5\x04\x00\x00\xe0\x94A*h\xf6\xc6EU\x9c\xc9w\xfcId\x04z \x1d\x1b\xb0\xe2\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4A?K\x02f\x9c\xcf\xf6\x80k\xc8&\xfc\xb7\xde\xca;\x0e\xa9\xbc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4AE\x99\t.\x87\x9a\xe2Sr\xa8MsZ\xf5\xc4\xe5\x10\xcdm\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4AHV\x12\xd04F\xecL\x05\xe5$NV?\x1c\xba\xe0\xf1\x97\x894\x95tD\xb8@\xe8\x00\x00\u07d4A]\tj\xb0b\x93\x18?<\x03=%\xf6\xcfqx\xac;\u01c9\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4Af\xfc\b\u0285\xf7f\xfd\xe81F\x0e\x9d\xc9<\x0e!\xaal\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ag\x84\xaf`\x960\xb0p\u051a\x8b\xcd\x12#\\d(\xa4\b\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Ag\xcdH\xe73A\x8e\x8f\x99\xff\xd14\x12\x1cJJ\xb2x\u0109\xc5S%\xcat\x15\xe0\x00\x00\u07d4Al\x86\xb7 \x83\xd1\xf8\x90}\x84\xef\xd2\xd2\u05c3\xdf\xfa>\xfb\x89lj\xccg\u05f1\xd4\x00\x00\u07d4AsA\x9d\\\x9fc)U\x1d\xc4\xd3\xd0\u03ac\x1bp\x1b\x86\x9e\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4At\xfa\x1b\xc1*;q\x83\u02eb\xb7z\vYU{\xa5\xf1\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4Axj\x10\xd4G\xf4\x84\xd32D\u0337\xfa\u034bB{[\x8c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Az<\u0454\x96S\nmB\x04\u00f5\xa1|\xe0\xf2\a\xb1\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4A~N&\x88\xb1\xfdf\xd8!R\x9eF\xedOB\xf8\xb3\xdb=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94A\x9aq\xa3l\x11\xd1\x05\xe0\xf2\xae\xf5\xa3\xe5\x98\a\x8e\x85\xc8\v\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94A\x9b\xdes\x16\xcc\x1e\u0495\u0205\xac\xe3B\u01db\xf7\xee3\xea\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4A\xa2\xf2\xe6\xec\xb8c\x94\xec\x0e3\x8c\x0f\xc9~\x9cU\x83\xde\u0489l\xee\x06\u077e\x15\xec\x00\x00\u07d4A\xa8\u0083\x00\x81\xb1\x02\xdfn\x011e|\a\xabc[T\u0389lj\xccg\u05f1\xd4\x00\x00\u07d4A\xa8\xe26\xa3\x0emc\xc1\xffdM\x13*\xa2\\\x89S~\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4A\xa9\xa4\x04\xfc\x9f[\xfe\xe4\x8e\xc2e\xb1%#3\x8e)\xa8\xbf\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4A\xad6\x9fu\x8f\xef8\xa1\x9a\xa3\x14\x93y\x83,\x81\x8e\xf2\xa0\x8966\x9e\xd7t}&\x00\x00\u07d4A\xb2\xd3O\xde\v\x10)&+Ar\xc8\x1c\x15\x90@[\x03\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4A\xb2\xdb\u05dd\u069b\x86Ojp0'T\x19\u00dd>\xfd;\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4A\xc3\xc26u4\xd1;\xa2\xb3?\x18\\\xdb\xe6\xacC\xc2\xfa1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4A\u02d8\x96D_p\xa1\n\x14!R\x96\xda\xf6\x14\xe3,\xf4\u0549g\x8a\x93 b\xe4\x18\x00\x00\u07d4A\xcey\x95\t5\xcf\xf5[\xf7\x8eL\xce\xc2\xfec\x17\x85\u06d5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4A\u04f71\xa3&\xe7hX\xba\xa5\xf4\xbd\x89\xb5{6\x93#C\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94A\xe4\xa2\x02u\xe3\x9b\xdc\xef\xebe\\\x03\"tKvQ@\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4A\xed-\x8ep\x81H,\x91\x9f\xc2=\x8f\x00\x91\xb3\xc8,F\x85\x89F:\x1ev[\u05ca\x00\x00\xe0\x94A\xf2~tK\u049d\xe2\xb0Y\x8f\x02\xa0\xbb\x9f\x98\xe6\x81\ua90a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4A\xf4\x89\xa1\xect{\u009c>_\x9d\x8d\xb9xw\xd4\u0474\xe9\x89\a?u\u0460\x85\xba\x00\x00\u07d4B\x0f\xb8n}+Q@\x1f\xc5\xe8\xc7 \x15\xde\xcbN\xf8\xfc.\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\x16\x84\xba\xa9\xc0\xb4\xb5\xf5S8\xe6\xf6\xe7\xc8\xe1F\xd4\x1c\xb7\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4B9\x96Y\xac\xa6\xa5\xa8c\xea\"E\xc93\xfe\x9a5\xb7\x88\x0e\x89n\xce2\xc2l\x82p\x00\x00\xe0\x94B;\xcaG\xab\xc0\fpW\xe3\xad4\xfc\xa6>7_\xbd\x8bJ\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4B<1\a\xf4\xba\xceANI\x9cd9\nQ\xf7F\x15\xca^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B<\xc4YL\xf4\xab\xb66\x8d\xe5\x9f\u04b1#\a4a!C\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BD\xf13\x11X\xb9\xce&\xbb\xe0\xb9#k\x92\x03\xca5\x144\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794BQw\xebt\xad\n\x9d\x9aWR\"\x81G\xeemcV\xa6\u6239\x8b\xc8)\xa6\xf9\x00\x00\u07d4BW%\xc0\xf0\x8f\b\x11\xf5\xf0\x06\xee\xc9\x1c\\\\\x12k\x12\xae\x89\b!\xab\rD\x14\x98\x00\x00\xe0\x94BX\xfdf/\xc4\xce2\x95\xf0\xd4\xed\x8f{\xb1D\x96\x00\xa0\xa9\x8a\x01lE.\xd6\b\x8a\xd8\x00\x00\xe0\x94B\\\x18\x16\x86\x8fww\xcc+\xa6\xc6\u048c\x9e\x1eylR\xb3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\\3\x8a\x13%\xe3\xa1W\x8e\xfa)\x9eW\u0646\xebGO\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94BbY\xb0\xa7Vp\x1a\x8bf5(R!V\xc0(\x8f\x0f$\x8a\x02\x18\xae\x19k\x8dO0\x00\x00\u07d4Bm\x15\xf4\a\xa0\x115\xb1:kr\xf8\xf2R\v51\xe3\x02\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Box\xf7\r\xb2Y\xac\x854\x14[)4\xf4\xef\x10\x98\xb5\u0609\x13\x84\x00\xec\xa3d\xa0\x00\x00\u07d4Bs-\x8e\xf4\x9f\xfd\xa0K\x19x\x0f\xd3\xc1\x84i\xfb7A\x06\x89\x17\v\x00\xe5\u4a7e\x00\x00\u07d4Bt\x17\xbd\x16\xb1\xb3\xd2-\xbb\x90-\x8f\x96W\x01o$\xa6\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Btj\xee\xa1O'\xbe\xff\f\r\xa6BS\xf1\xe7\x97\x18\x90\xa0\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4B{F*\xb8NP\x91\xf4\x8aF\xeb\f\u0712\xdd\xcb&\xe0x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B~GQ\u00fa\xbex\xcf\xf8\x83\b\x86\xfe\xbc\x10\xf9\x90\x8dt\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94B~\xc6h\xac\x94\x04\xe8\x95\u0306\x15\x11\xd1b\nI\x12\xbe\x98\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4B\x80\xa5\x8f\x8b\xb1\v\x94@\u0794\xf4+OY! \x82\x01\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B\x8a\x1e\xe0\xed3\x1dyR\u033e\x1cyt\xb2\x85+\u0453\x8a\x89w\xb7JN\x8d\xe5e\x00\x00\u0794B\x9c\x06\xb4\x87\xe8Tj\xbd\xfc\x95\x8a%\xa3\xf0\xfb\xa5?o\x00\x88\xbbdJ\xf5B\x19\x80\x00\xe0\x94B\xa9\x8b\xf1`'\xceX\x9cN\xd2\xc9X1\xe2rB\x05\x06N\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4B\xc6\xed\xc5\x15\xd3UW\x80\x8d\x13\xcdD\xdc\xc4@\v%\x04\xe4\x89\n\xba\x14\u015b\xa72\x00\x00\u07d4B\xce\xcf\u0492\x10y\xc2\xd7\xdf?\b\xb0z\xa3\xbe\xee^!\x9a\x8965\u026d\xc5\u07a0\x00\x00\u07d4B\u04669\x9b0\x16\xa8Y\u007f\x8bd\t'\xb8\xaf\xbc\xe4\xb2\x15\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4B\xd3I@\xed\xd2\xe7\x00]F\xe2\x18\x8eL\xfe\u0383\x11\xd7M\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4B\u04e5\xa9\x01\xf2\xf6\xbd\x93V\xf1\x12\xa7\x01\x80\xe5\xa1U\v`\x892$\xf4'#\xd4T\x00\x00\u07d4B\u05b2c\xd9\xe9\xf4\x11lA\x14$\xfc\x99Ux<v00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4B\xdb\v\x90%Y\xe0@\x87\xdd\\D\x1b\xc7a\x194\x18K\x89\x89m3\xb1}%:b\x00\x00\u07d4B\xdd\xd0\x14\xdcR\xbf\xbc\xc5U2Z@\xb5\x16\xf4\x86j\x1d\u04c9lk\x93[\x8b\xbd@\x00\x00\u07d4C\x19&?u@,\vS%\xf2c\xbeJP\x80e\x10\x87\xf0\x895K\x0f\x14c\x1b\xab\x00\x00\u07d4C\x1f,\x19\xe3\x16\xb0D\xa4\xb3\xe6\x1a\fo\xf8\xc1\x04\xa1\xa1/\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94C\"}e3Ni\x1c\xf21\xb4\xa4\xe1\xd39\xb9]Y\x8a\xfb\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94C(\t\xa29\x0f\a\xc6e\x92\x1f\xf3}T}\x12\xf1\u0256j\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4C)\xfc\t1\xcb\xeb\x038\x80\xfeL\x93\x98\xcaE\xb0\xe2\xd1\x1a\x89lq qm3h\x00\x00\u07d4C-\x88K\u059d\xb1\xac\xc0\u061cd\xad\xe4\xcbO\u00e8\x8bz\x89\x86\x9a\x8c\x10\x80\x8e\xec\x00\x00\u07d4C1\xab7G\xd3W \xa9\xd8\xca%\x16\\\u0485\xac\u053d\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C:;h\xe5k\r\xf1\x86+\x90Xk\xbd9\xc8@\xff\x196\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94C>;\xa1\xc5\x1b\x81\x0f\xc4g\u057aM\xeaB\xf7\xa9\x88^i\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94C>\xb9J3\x90\x86\xed\x12\u067d\xe9\xcd\x1dE\x86\x03\xc9}\u058a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4CI\"Zb\xf7\n\xeaH\n\x02\x99\x15\xa0\x1eSy\xe6O\xa5\x89\x8c\xd6~#4\xc0\xd8\x00\x00\u07d4CT\"\x1eb\xdc\t\xe6@d6\x16:\x18^\xf0m\x11J\x81\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94CTC\xb8\x1d\xfd\xb9\xbd\x8cg\x87\xbc%\x18\xe2\xd4~W\xc1_\x8a\x01C\x8d\x93\x97\x88\x1e\xf2\x00\x00\u07d4Ca\u0504o\xaf\xb3w\xb6\xc0\xeeI\xa5\x96\xa7\x8d\xdf5\x16\xa3\x89\xc2\x12z\xf8X\xdap\x00\x00\xe0\x94Cd0\x9a\x9f\xa0p\x95`\x0fy\xed\xc6Q \xcd\xcd#\xdcd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Cg\xaeK\f\xe9d\xf4\xa5J\xfdK\\6\x84\x96\xdb\x16\x9e\x9a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Ct\x89(\xe8\xc3\xecD6\xa1\u0412\xfb\xe4:\xc7I\xbe\x12Q\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4Cv{\xf7\xfd*\xf9[r\xe91-\xa9D<\xb1h\x8eCC\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94Cy\x838\x8a\xb5\x9aO\xfc!_\x8e\x82iF\x10)\xc3\xf1\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4C\x89\x8cI\xa3MP\x9b\xfe\xd4\xf7`A\xee\x91\xca\xf3\xaaj\xa5\x89\x10CV\x1a\x88)0\x00\x00\u07d4C\x8c/T\xff\x8eb\x9b\xab6\xb1D+v\v\x12\xa8\x8f\x02\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\x98b\x8e\xa6c-9>\x92\x9c\xbd\x92\x84d\xc5h\xaaJ\f\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4C\x9d//Q\x10\xa4\u054b\x17W\x93P\x15@\x87@\xfe\xc7\xf8\x89\u03e5\xc5\x15\x0fL\x88\x80\x00\u07d4C\x9d\xee?vy\xff\x100s?\x93@\xc0\x96hkI9\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xb0y\xba\xf0ry\x99\xe6k\xf7C\u057c\xbfwl;\t\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xbc-M\xdc\xd6X;\xe2\u01fc\tK(\xfbr\xe6+\xa8;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xc7\xeb\u0173\xe7\xaf\x16\xf4}\xc5az\xb1\x0e\x0f9\xb4\xaf\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4C\u02d6R\x81\x8coMg\x96\xb0\xe8\x94\t0ly\xdbcI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4C\xcc\b\xd0s*\xa5\x8a\xde\xf7a\x9b\xedFU\x8a\xd7wAs\x89\xf0\xe7\u0730\x12*\x8f\x00\x00\xe0\x94C\u0567\x1c\xe8\xb8\xf8\xae\x02\xb2\xea\xf8\xea\xf2\xca(@\xb9?\xb6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794C\xdb\u007f\xf9Z\bm(\ubff8/\xb8\xfb_#\n^\xbc\u0348\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4C\xe7\xec\x84cX\xd7\xd0\xf97\xad\x1c5\v\xa0i\u05ffr\xbf\x89\x06p\xaeb\x92\x14h\x00\x00\u07d4C\xf1o\x1eu\xc3\xc0j\x94x\xe8\u0157\xa4\n<\xb0\xbf\x04\u0309\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4C\xf4p\xede\x9e)\x91\xc3u\x95~]\xde\u017d\x1d8\"1\x89\x05k\xc7^-c\x10\x00\x00\u07d4C\xf7\xe8n8\x1e\xc5\x1e\u0110m\x14v\u02e9z=\xb5\x84\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4C\xff8t>\xd0\xcdC0\x8c\x06e\t\u030e~r\xc8b\xaa\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94C\xff\x88S\xe9\x8e\xd8@k\x95\x00\n\u0684\x83b\u05a09*\x8a\x04\xae\v\x1cM.\x84\xd0\x00\x00\u07d4D\t\x88f\xa6\x9bh\xc0\xb6\xbc\x16\x82)\xb9`5\x87\x05\x89g\x89\n1\x06+\xee\xedp\x00\x00\u07d4D\x19\xaca\x8d]\xea|\xdc`w o\xb0}\xbd\xd7\x1c\x17\x02\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4D\x1aR\x00\x16a\xfa\xc7\x18\xb2\u05f3Q\xb7\xc6\xfbR\x1az\xfd\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94D\x1a\u0282c\x13$\xac\xbf\xa2F\x8b\xda2[\xbdxG{\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4D\x1f7\xe8\xa0)\xfd\x02H/(\x9cI\xb5\xd0m\x00\xe4\b\xa4\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4D \xaa5F[\xe6\x17\xad$\x98\xf3p\xde\n<\xc4\xd20\xaf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4D#/\xf6m\xda\xd1\xfd\x84\x12f8\x006\xaf\xd7\xcf}\u007fB\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D%\rGn\x06$\x84\xe9\b\n9g\xbf:Js*\xd7?\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4D)\xa2\x9f\xee\x19\x84Pg,\f\x1d\a1b%\v\xecdt\x896*\xaf\x82\x02\xf2P\x00\x00\u07d4D5RS\xb2wH\xe3\xf3O\xe9\xca\xe1\xfbq\x8c\x8f$\x95)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4D8\xe8\x80\xcb'f\xb0\xc1\u03ae\xc9\xd2A\x8f\u03b9R\xa0D\x89\a?\xa0s\x90?\b\x00\x00\u07d4DL\xafy\xb7\x138\ue6a7\xc73\xb0*\u02a7\xdc\x02YH\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4D\\\xb8\xde^=\xf5 \xb4\x99\xef\u0240\xf5+\xff@\xf5\\v\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Dj\x809\xce\u03dd\xceHy\xcb\xca\xf3I;\xf5E\xa8\x86\x10\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4Dt)\x9d\x0e\xe0\x90\u0710x\x9a\x14\x86H\x9c=\rd^m\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\x8b\xf4\x10\xad\x9b\xbc/\xec\xc4P\x8d\x87\xa7\xfc.K\x85a\xad\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4D\x90\x1e\r\x0e\b\xac=^\x95\xb8\xec\x9d^\x0f\xf5\xf1.\x03\x93\x89\x16\xa1\xf9\xf5\xfd}\x96\x00\x00\xe0\x94D\x93\x12<\x02\x1e\xce;3\xb1\xa4R\xc9&\x8d\xe1@\a\xf9\u04ca\x01je\x02\xf1Z\x1eT\x00\x00\xe0\x94D\x9a\xc4\xfb\xe3\x83\xe3g8\x85^6JW\xf4q\xb2\xbf\xa11\x8a)\xb7d2\xb9DQ \x00\x00\u07d4D\xa0\x1f\xb0J\xc0\xdb,\xce]\xbe(\x1e\x1cF\xe2\x8b9\xd8x\x89lj\xccg\u05f1\xd4\x00\x00\u07d4D\xa6=\x18BE\x87\xb9\xb3\a\xbf\xc3\xc3d\xae\x10\xcd\x04\xc7\x13\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94D\xa8\x98\x9e20\x81!\xf7$f\x97\x8d\xb3\x95\xd1\xf7l:K\x8a\x01\x88P)\x9fB\xb0j\x00\x00\u07d4D\xc1\x11\v\x18\x87\x0e\xc8\x11x\xd9=!X8\xc5Q\u050ed\x89\n\xd6\xf9\x85\x93\xbd\x8f\x00\x00\u07d4D\xc1Ge\x12|\xde\x11\xfa\xb4l],\xf4\u0532\x89\x00#\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94D\xc5N\xaa\x8a\xc9@\xf9\xe8\x0f\x1et\xe8/\xc1O\x16v\x85j\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4D\xcdwSZ\x89?\xa7\xc4\xd5\xeb:$\x0ey\u0419\xa7--\x89,s\xc97t,P\x00\x00\u07d4D\u07faP\xb8)\xbe\xcc_O\x14\u0470J\xab3 \xa2\x95\xe5\x8965\u026d\xc5\u07a0\x00\x00\u07d4D\xe2\xfd\xc6y\xe6\xbe\xe0\x1e\x93\xefJ:\xb1\xbc\xce\x01*\xbc|\x89\x16=\x19I\x00\xc5E\x80\x00\xe0\x94D\xf6/*\xaa\xbc)\xad:k\x04\xe1\xffo\x9c\xe4R\xd1\xc1@\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4D\xff\xf3{\xe0\x1a8\x88\u04f8\xb8\u1200\xa7\xdd\xef\xee\xea\u04c9\x0e\f[\xfc}\xae\x9a\x80\x00\u07d4E\x06\xfe\x19\xfaK\x00k\xaa9\x84R\x9d\x85\x16\xdb++P\xab\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\x1b6\x99G[\xed]y\x05\xf8\x90Z\xa3Eo\x1e\u05c8\xfc\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u0794E\x1bpp%\x9b\u06e2q\x00\xe3n#B\x8aS\xdf\xe3\x04\u9239\x8b\xc8)\xa6\xf9\x00\x00\u07d4E'+\x8fb\xe9\xf9\xfa\x8c\xe0D \u1ba3\xeb\xa9hn\xac\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94E+d\u06ce\xf7\xd6\u07c7\u01c8c\x9c\"\x90\xbe\x84\x82\xd5u\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E>5\x9a3\x97\x94LZ'Z\xb1\xa2\xf7\n^Z?i\x89\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4EI\xb1Yy%_~e\xe9\x9b\rV\x04\u06d8\xdf\xca\u023f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4EKa\xb3D\xc0\xef\x96Qy#\x81U\xf2w\u00c2\x9d\v8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94EO\x01A\xd7!\xd3<\xbd\xc4\x10\x18\xbd\x01\x11\x9a\xa4xH\x18\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4ES3\x90\xe3@\xfe\r\xe3\xb3\xcf_\xb9\xfc\x8e\xa5R\xe2\x9eb\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4ES\x96\xa4\xbb\u067a\u8bdf\xb7\xc4\xd6MG\x1d\xb9\xc2E\x05\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4E[\x92\x96\x92\x1at\xd1\xfcAa\u007fC\xb80>o>\xd7l\x89\u3bb5sr@\xa0\x00\x00\u07d4E\\\xb8\xee9\xff\xbcu#1\xe5\xae\xfcX\x8e\xf0\xeeY4T\x8965F:x\r\xef\x80\x00\u07d4Ej\u0b24\x8e\xbc\xfa\xe1f\x06\x02PR_c\x96^v\x0f\x89\x10CV\x1a\x88)0\x00\x00\u07d4Eo\x8dtf\x82\xb2$g\x93I\x06M\x1b6\x8c|\x05\xb1v\x89\u0213\u041c\x8fQP\x00\x00\u07d4Ep)\xc4i\xc4T\x8d\x16\x8c\xec>e\x87.D(\xd4+g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Eq\xdeg+\x99\x04\xba\xd8t6\x92\xc2\x1cO\xdc\xeaL.\x01\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Ex\x1b\xbew\x14\xa1\xc8\xf7;\x1cty!\xdfO\x84'\x8bp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E{\xce\xf3}\xd3\xd6\v-\xd0\x19\xe3\xfea\xd4k?\x1erR\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94E\x8e<\u025e\x94xD\xa1\x8ejB\x91\x8f\xef~\u007f_^\xb3\x8a\a\xb5?y\xe8\x88\xda\xc0\x00\x00\u07d4E\x93\x93\xd6:\x06>\xf3r\x1e\x16\xbd\x9f\xdeE\ue77dw\xfb\x89j\xba\u05a3\xc1S\x05\x00\x00\u07d4E\xa5p\xdc\xc2\t\f\x86\xa6\xb3\xea)\xa6\bc\xdd\xe4\x1f\x13\xb5\x89\f\x9a\x95\xee)\x86R\x00\x00\u07d4E\xa8 \xa0g/\x17\xdct\xa0\x81\x12\xbcd?\xd1\x16w6\u00c9\n\xd6\xc4;(\x15\xed\x80\x00\u07d4E\xb4q\x05\xfeB\xc4q-\xcen*!\xc0[\xff\xd5\xeaG\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\xbb\x82\x96R\u063f\xb5\x8b\x85'\xf0\xec\xb6!\u009e!.\u00c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\xc0\u045f\v\x8e\x05O\x9e\x8986\xd5\xec\xaey\x01\xaf(\x12\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4E\xc4\xec\xb4\xee\x89\x1e\xa9\x84\xa7\xc5\xce\xfd\x8d\xfb\x001\v(P\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4E\u028d\x95f\b\xf9\xe0\n/\x99t\x02\x86@\x88\x84ef\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u0298b\x00;N@\xa3\x17\x1f\xb5\xca\xfa\x90(\xca\xc8\xde\x19\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4E\xd1\xc9\xee\xdf|\xabA\xa7y\x05{y9_T(\xd8\x05(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4E\u0535M7\xa8\xcfY\x98!#_\x06/\xa9\xd1p\xed\u8909\x11\x90g;_\u0690\x00\x00\xe0\x94E\xdb\x03\xbc\xcf\u05a5\xf4\xd0&k\x82\xa2*6\x87\x92\xc7}\x83\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4E\xe3\xa9>r\x14J\u0686\f\xbcV\xff\x85\x14Z\xda8\xc6\u0689WG=\x05\u06ba\xe8\x00\x00\u07d4E\u6378\u06fa\xba_\xc2\xcb3|b\xbc\xd0\xd6\x1b\x05\x91\x89\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94E\u6379L}\n\xb7\xacA\x85zq\xd6qG\x87\x0fNq\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4E\xf4\xfc`\xf0\x8e\xac\xa1\x05\x98\xf03c)\x80\x1e<\x92\xcbF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F\rSU\xb2\xce\xebnb\x10}\x81\xe5\x12p\xb2k\xf4V \x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94F\"O2\xf4\xec\xe5\u0206p\x90\xd4@\x9dU\xe5\v\x18C-\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4F'\xc6\x06\x84&q\xab\u0782\x95\xee]\xd9L\u007fT\x954\xf4\x89\x0f\x89_\xbd\x872\xf4\x00\x00\u07d4F+g\x8bQ\xb5\x84\xf3\xedz\xda\a\v\\\u065c\v\xf7\xb8\u007f\x89\x05k\xc7^-c\x10\x00\x00\u07d4FM\x9c\x89\xcc\xe4\x84\xdf\x00\x02w\x19\x8e\xd8\a_\xa65r\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4FPNj!Z\xc8;\xcc\xf9V\xbe\xfc\x82\xabZg\x93q\u0209\x1c!(\x05\u00b4\xa5\x00\x00\xe0\x94FQ\xdcB\x0e\b\xc3);'\xd2Ix\x90\xebP\":\xe2\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4FS\x1e\x8b\x1b\xde\t\u007f\u07c4\x9dm\x11\x98\x85`\x8a\x00\x8d\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Fb\x92\xf0\xe8\rC\xa7\x87t'u\x90\xa9\xebE\x96\x12\x14\xf4\x894\x95tD\xb8@\xe8\x00\x00\xe0\x94Fb\xa1v^\xe9!\x84-\u0708\x89\x8d\x1d\xc8bu\x97\xbd~\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Fe\xe4s\x96\xc7\u06d7\xeb*\x03\xd9\bc\xd5\u053a1\x9a\x94\x89 \x86\xac5\x10R`\x00\x00\u07d4Fo\xdak\x9bX\xc5S'P0j\x10\xa2\xa8\xc7h\x10;\a\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4Fq$\xae\u007fE/&\xb3\xd5t\xf6\b\x88\x94\xfa]\x1c\xfb;\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u0794Fr*6\xa0\x1e\x84\x1d\x03\xf7\x80\x93^\x91}\x85\u0566z\xbd\x88\xce\xc7o\x0eqR\x00\x00\u07d4Fw\x9aVV\xff\x00\xd7>\xac:\xd0\u00cbl\x850\x94\xfb@\x89\f\x82S\xc9lj\xf0\x00\x00\u07d4Fw\xb0N\x03C\xa3!1\xfdj\xbb9\xb1\xb6\x15k\xba=[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4F}Y\x88$\x9ahaG\x16e\x98@\xed\n\xe6\xf6\xf4W\xbc\x89\x15\x01\xa4\x8c\xef\xdf\xde\x00\x00\u07d4F~\x0e\xd5O;v\xae\x066\x17n\aB\b\x15\xa0!sn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F~\xa1\x04E\x82~\xf1\xe5\x02\xda\xf7k\x92\x8a \x9e\r@2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94F\u007f\xbfAD\x16\x00u\u007f\xe1X0\xc8\xcd_O\xfb\xbb\xd5`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94F\x93Xp\x932\xc8+\x88~ \xbc\xdd\xd0\"\x0f\x8e\u06e7\u040a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4F\x97\xba\xaf\x9c\xcb`?\xd3\x040h\x9dCTE\xe9\u024b\xf5\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4F\xa3\v\x8a\x80\x891!tE\xc3\xf5\xa9>\x88,\x03E\xb4&\x89\r\x8d\xb5\xeb\u05f2c\x80\x00\u07d4F\xa40\xa2\u0528\x94\xa0\u062a?\xea\xc6\x156\x14\x15\xc3\xf8\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4F\xaaP\x18pg~\u007f\nPHv\xb4\xe8\x80\x1a\n\xd0\x1cF\x89+^:\xf1k\x18\x80\x00\x00\u07d4F\xbf\u0172\a\xeb \x13\xe2\xe6\x0fw_\xec\xd7\x18\x10\u0159\f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4F\xc1\xaa\"D\xb9\u0229W\u028f\xacC\x1b\x05\x95\xa3\xb8h$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4F\xd8\x061(B\x03\xf6(\x8e\xcdNWX\xbb\x9dA\xd0]\xbe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\n\xc5\xd1\xf3\xef\xe2\x8f8\x02\xaf\x92[W\x1ec\x86\x8b9}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\x10\x10\xdaI/@\x18\x83;\b\x8d\x98r\x90\x1e\x06\x12\x91t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4G\x12T\x02e\xcb\xee\u00c4p\"\u015f\x1b1\x8dC@\n\x9e\x89\xbd\xbcA\xe04\x8b0\x00\x00\xe0\x94G\x14\u03e4\xf4k\u05bdps}u\x87\x81\x97\xe0\x8f\x88\xe61\x8a\x02\u007f>\u07f3Nn@\x00\x00\u07d4G H\xcc`\x9a\xeb$!e\uaa87\x05\x85\f\xf3\x12]\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4G!\x92)\xe8\xcdVe\x9ae\u00a9C\xe2\u075a\x8fK\xfd\x89\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4G7\xd0B\xdcj\xe7>\xc7:\xe2Qz\u03a2\xfd\xd9d\x87\u014965\u026d\xc5\u07a0\x00\x00\u07d4GAX\xa1\xa9\xdci<\x13?e\xe4{\\:\xe2\xf7s\xa8o\x89\n\xdaUGK\x814\x00\x00\u07d4GE\xab\x18\x1a6\xaa\x8c\xbf\"\x89\xd0\xc4Qe\xbc~\xbe#\x81\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4GPf\xf9\xad&eQ\x96\xd5SS'\xbb\xeb\x9by)\xcb\x04\x89\xa4\xccy\x95c\u00c0\x00\x00\xe0\x94GR!\x8eT\xdeB?\x86\xc0P\x193\x91z\xea\b\xc8\xfe\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4GZa\x93W-JNY\u05fe\t\u02d6\r\u074cS\x0e/\x89$,\xf7\x8c\xdf\a\xff\x80\x00\u07d4Gd\x8b\xed\x01\xf3\xcd2I\bNc]\x14\u06a9\xe7\xec<\x8a\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4Gh\x84\x10\xff%\xd6T\xd7.\xb2\xbc\x06\xe4\xad$\xf83\xb0\x94\x89\b\xb2\x8da\xf3\u04ec\x00\x00\u07d4GkU\x99\b\x9a?\xb6\xf2\x9clr\xe4\x9b.G@\ua00d\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4Gs\x0f_\x8e\xbf\x89\xacr\xef\x80\xe4l\x12\x19P8\xec\xdcI\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94G{$\xee\u80deO\u045d\x12P\xbd\vfEyJa\u028a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4G\x81\xa1\nM\xf5\uef02\xf4\xcf\xe1\a\xba\x1d\x8av@\xbdf\x89a\t=|,m8\x00\x00\u07d4G\x88Z\xba\xbe\xdfM\x92\x8e\x1c<q\xd7\xca@\xd5c\xedY_\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4G\x8d\xc0\x9a\x13\x117|\t?\x9c\u022et\x11\x1fe\xf8/9\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94G\x8eRN\xf2\xa3\x81\xd7\f\x82X\x8a\x93\xcaz_\xa9\xd5\x1c\xbf\x8a5\xfa\x97\"o\x88\x99p\x00\x00\xe0\x94G\x92\x98\xa9\xde\x14~c\xa1\xc7\xd6\xd2\xfc\xe0\x89\xc7\xe6@\x83\xbd\x8a\x02\x1e\x19\xdd<<\ry\x80\x00\u07d4G\x9a\xbf-\xa4\u0547\x16\xfd\x97:\r\x13\xa7_S\x01P&\n\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4G\xa2\x81\xdf\xf6Ag\x19xU\xbfnp^\xb9\xf2\xce\xf62\xea\x8966\xc9yd6t\x00\x00\u07d4G\xbe\xb2\x0fu\x91\x00T*\xa9=A\x11\x8b2\x11\xd6d\x92\x0e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94G\xc2G\xf5;\x9f\xbe\xb1{\xba\a\x03\xa0\f\x00\x9f\xdb\x0fn\xae\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794G\xc7\xe5\ufd0b:\xedK|n\x82KC_5}\xf4\xc7#\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4G\u03dc\xda\xf9/\u0259\xcc^\xfb\xb7 <a\xe4\xf1\xcd\xd4\u00c9\a\x1f\x8a\x93\xd0\x1eT\x00\x00\u07d4G\xd2\x0ej\xe4\xca\xd3\xf8)\xea\xc0~Z\xc9{f\xfd\xd5l\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4G\u05d2\xa7Vw\x9a\xed\xf14>\x88\x83\xa6a\x9cl(\x11\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94G\xe2]\xf8\x82%8\xa8Yk(\xc67\x89kM\x14<5\x1d\x8a\x11\v\xe9\xeb$\xb8\x81P\x00\x00\u07d4G\xf4ik\xd4b\xb2\r\xa0\x9f\xb8>\xd2\x03\x98\x18\xd7v%\xb3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4G\xfe\xf5\x85\x84FRH\xa0\x81\r`F>\xe9>Zn\xe8\u04c9\x0fX\xcd>\x12i\x16\x00\x00\u07d4G\xffo\xebC! `\xbb\x15\x03\u05e3\x97\xfc\b\xf4\xe7\x03R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4G\xff\xf4,g\x85Q\xd1A\xebu\xa6\xee9\x81\x17\xdf>J\x8d\x89\x05k\xea\xe5\x1f\xd2\xd1\x00\x00\u07d4H\x01\x0e\xf3\xb8\xe9^?0\x8f0\xa8\xcb\u007fN\xb4\xbf`\xd9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4H\n\xf5 v\x00\x9c\xa77\x81\xb7\x0eC\xb9Y\x16\xa6\"\x03\xab\x892\x19r\xf4\b=\x87\x80\x00\u07d4H\x0f1\xb9\x891\x1eA$\u01a7F_ZD\tM6\xf9\u04097\x90\xbb\x85Q7d\x00\x00\xe0\x94H\x11\x15)j\xb7\xdbRI/\xf7\xb6G\xd63)\xfb\\\xbck\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4H\x1e:\x91\xbf\xdc/\x1c\x84(\xa0\x11\x9d\x03\xa4\x16\x01A~\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4H(\xe4\xcb\xe3N\x15\x10\xaf\xb7,+\ueb0aE\x13\xea\xeb\u0649\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94H)\x82\xac\x1f\x1cm\x17!\xfe\xec\u0679\xc9l\xd9I\x80PU\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4H0,1\x1e\xf8\xe5\xdcfAX\xddX<\x81\x19Mn\rX\x89\xb6gl\xe0\xbc\xcb\\\x00\x00\u07d4H;\xa9\x904\xe9\x00\xe3\xae\xdfaI\x9d;+\xce9\xbe\xb7\xaa\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4HT\x8bK\xa6+\xcb/\r4\xa8\x8d\u019ah\x0eS\x9c\xf0F\x89\x05l\xf1\u02fbt2\x00\x00\u07d4Hc\x84\x979&Zc\xb0\xa2\xbf#jY\x13\xe6\xf9Y\xce\x15\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4He\x9d\x8f\x8c\x9a/\xd4Oh\u06a5]#\xa6\b\xfb\xe5\x00\u0709lk\x93[\x8b\xbd@\x00\x00\xe0\x94Hf\x9e\xb5\xa8\x01\u0637_\xb6\xaaX\xc3E\x1bpX\xc2C\xbf\x8a\x06\x8dB\xc18\u06b9\xf0\x00\x00\u07d4Hjl\x85\x83\xa8D\x84\xe3\xdfC\xa1#\x83\u007f\x8c~#\x17\u0409\x11\x87\xc5q\xab\x80E\x00\x00\u07d4Hz\xdf}p\xa6t\x0f\x8dQ\xcb\xddh\xbb?\x91\u0125\xceh\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4H~\x10\x85\x02\xb0\xb1\x89\uf70cm\xa4\xd0\xdbba\xee\xc6\xc0\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94H\x88\xfb%\xcdP\u06f9\xe0H\xf4\x1c\xa4}x\xb7\x8a'\xc7\u064a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u0794H\x934\u00b6\x95\xc8\xee\a\x94\xbd\x86B\x17\xfb\x9f\xd8\xf8\xb15\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4H\xa3\r\xe1\xc9\x19\xd3\xfd1\x80\xe9}_+*\x9d\xbd\x96M-\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4H\xbf\x14\u05f1\xfc\x84\xeb\xf3\xc9k\xe1/{\xce\x01\xaai\xb0>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4H\xc2\ue465\aV\xd8\u039a\xbe\xebu\x89\xd2,o\xee]\xfb\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4H\xc5\u0197\v\x91a\xbb\x1c{z\xdf\xed\x9c\xde\u078a\x1b\xa8d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94H\xd2CKz}\xbb\xff\b\";c\x87\xb0]\xa2\xe5\t1&\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4H\xd4\xf2F\x8f\x96?\u05da\x00a\x98\xbbg\x89]-Z\xa4\u04c9K\xe4\xe7&{j\xe0\x00\x00\u07d4H\xe0\xcb\xd6\u007f\x18\xac\xdbzb\x91\xe1%M\xb3.\trs\u007f\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4H\xf6\n5HO\xe7y+\u030a{c\x93\xd0\u0761\xf6\xb7\x17\x89\xc3(\t>a\xee@\x00\x00\u07d4H\xf8\x83\xe5g\xb46\xa2{\xb5\xa3\x12M\xbc\x84\xde\xc7u\xa8\x00\x89)\xd7n\x86\x9d\u0340\x00\x00\xe0\x94I\x01E\xaf\xa8\xb5E\"\xbb!\xf3R\xf0m\xa5\xa7\x88\xfa\x8f\x1d\x8a\x01\xf4lb\x90\x1a\x03\xfb\x00\x00\u07d4I\t\xb3\x19\x98\xea\xd4\x14\xb8\xfb\x0e\x84k\xd5\xcb\xde995\xbe\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x12\xd9\x02\x93\x16v\xff9\xfc4\xfe<<\xc8\xfb!\x82\xfaz\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4I\x13o\xe6\xe2\x8btS\xfc\xb1kk\xbb\u9aac\xba\x837\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94I\x15a\u06cbo\xaf\xb9\x00~b\xd0P\u0082\xe9,Kk\u020a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4I\x18]\xd7\xc262\xf4lu\x94s\ubb96`\b\xcd5\x98\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4I,\xb5\xf8a\xb1\x87\xf9\xdf!\xcdD\x85\xbe\xd9\vP\xff\xe2-\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4I-\xe4j\xaf\x8f\x1dp\x8dY\u05da\xf1\xd0:\xd2\xcb`\x90/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I.p\xf0M\x18@\x8c\xb4\x1e%`70Pk5\xa2\x87k\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4I:g\xfe#\xde\xccc\xb1\r\xdau\xf3(v\x95\xa8\x1b\u056b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4I=H\xbd\xa0\x15\xa9\xbf\xcf\x16\x03\x93n\xabh\x02L\xe5Q\xe0\x89\x018\xa3\x88\xa4<\x00\x00\x00\xe0\x94IBV\xe9\x9b\x0f\x9c\xd6\xe5\xeb\xca8\x99\x862R\x90\x01e\u020a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4IM\xecM^\xe8\x8a'q\xa8\x15\xf1\xeerd\x94/\xb5\x8b(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I[d\x1b\x1c\u07a3b\u00f4\u02fd\x0f\\\xc5\v\x1e\x17k\x9c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94Ih\xa2\xce\xdbEuU\xa19)Z\xea(wnT\x00<\x87\x8a\x02#\x1a\xef\u0266b\x8f\x00\x00\u07d4Im6U4S\n_\xc1W|\nRA\u02c8\xc4\xdapr\x89a\t=|,m8\x00\x00\xe0\x94In1\x95\x92\xb3A\xea\xcc\xd7x\u0767\xc8\x19mT\xca\xc7u\x8a\x01\xf5q\x89\x87fKH\x00\x00\u07d4IoXC\xf6\xd2L\u064d%^L#\xd1\xe1\xf0#\"uE\x89_\x17\x9f\u0526\xee\t\x80\x00\xe0\x94Ip\u04ec\xf7+[\x1f2\xa7\x00<\xf1\x02\xc6N\xe0TyA\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\u07d4Iw\xa7\x93\x9d\t9h\x94U\xce&9\xd0\xeeZL\xd9\x10\xed\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4Iy\x19N\xc9\xe9}\xb9\xbe\xe84;|w\xd9\xd7\xf3\xf1\u071f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Iy4c\xe1h\x10\x83\u05ab\xd6\xe7%\u057b\xa7E\xdc\xcd\xe8\x89\x1d\x98\xe9LNG\x1f\x00\x00\u07d4I\x81\xc5\xfff\xccN\x96\x80%\x1f\xc4\xcd/\xf9\a\xcb2xe\x89(\xa8WBTf\xf8\x00\x00\u07d4I\x89\u007f\xe92\xbb\xb3\x15L\x95\u04fc\xe6\xd9;ms)\x04\u0749\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4I\x89\xe1\xab^|\xd0\aF\xb3\x93\x8e\xf0\xf0\xd0d\xa2\x02[\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4I\x8a\xbd\xeb\x14\xc2k{r4\xd7\x0f\u03ae\xf3a\xa7m\xffr\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4I\xa6E\xe0f}\xfd{2\xd0u\xcc$g\u074ch\t\a\u0109\a\x06\x01\x95\x8f\u02dc\x00\x00\xe0\x94I\xb7N\x16\x92e\xf0\x1a\x89\xecL\x90r\u0164\xcdr\xe4\xe85\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4I\xbd\xbc{\xa5\xab\xeb\xb68\x9e\x91\xa3(R \xd3E\x1b\xd2S\x8965\u026d\xc5\u07a0\x00\x00\u07d4I\xc9A\xe0\xe5\x01\x87&\xb7)\x0f\xc4s\xb4q\xd4\x1d\xae\x80\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94I\xc9w\x1f\xca\x19\u0579\xd2E\u0211\xf8\x15\x8f\xe4\x9fG\xa0b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4I\xcf\x1eT\xbe61\x06\xb9 r\x9d-\v\xa4o\bg\x98\x9a\x89\x0e\x87?D\x13<\xb0\x00\x00\u07d4I\xd2\u008e\xe9\xbcT^\xaa\xf7\xfd\x14\xc2|@s\xb4\xbb_\x1a\x89O\xe9\xb8\x06\xb4\r\xaf\x00\x00\u07d4I\xdd\xee\x90.\x1d\f\x99\u0471\x1a\xf3\u030a\x96\xf7\x8eM\xcf\x1a\x89\n\u03a5\xe4\xc1\x8cS\x00\x00\u07d4I\xf0(9[Z\x86\xc9\xe0\u007fwxc\x0eL.=7:w\x89\x06\xa7JP8\u06d1\x80\x00\xe0\x94J\x19 5\xe2a\x9b$\xb0p\x9dVY\x0e\x91\x83\xcc\xf2\xc1\u064a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4J@S\xb3\x1d\x0e\xe5\u06ef\xb1\xd0k\u05ec\u007f\xf3\",G\u0589K\xe4\xe7&{j\xe0\x00\x00\u07d4JC\x01p\x15-\xe5\x17&3\u0742b\xd1\a\xa0\xaf\xd9j\x0f\x89\xabM\xcf9\x9a:`\x00\x00\u07d4JG\xfc>\x17\u007fVz\x1e8\x93\xe0\x00\xe3k\xba#R\n\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4JR\xba\xd2\x03W\"\x8f\xaa\x1e\x99k\xedy\f\x93gK\xa7\u0409Hz\x9a0E9D\x00\x00\u07d4JS\xdc\xdbV\xceL\xdc\xe9\xf8.\xc0\xeb\x13\xd6sR\xe7\u020b\x89\u3bb5sr@\xa0\x00\x00\u07d4J_\xae;\x03r\xc20\xc1%\xd6\xd4p\x14\x037\xab\x91VV\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4Jq\x90a\xf5(T\x95\xb3{\x9d~\xf8\xa5\x1b\a\xd6\u6b2c\x89\n\xd4\xc81j\v\f\x00\x00\u07d4Js8\x92\x98\x03\x1b\x88\x16\u0329FB\x1c\x19\x9e\x18\xb3C\u0589\"8h\xb8y\x14o\x00\x00\u07d4Js]\"G\x927m3\x13g\xc0\x93\xd3\x1c\x87\x944\x15\x82\x89f\xff\xcb\xfd^Z0\x00\x00\u07d4Jt\x94\xcc\xe4HU\u0300X(B\xbe\x95\x8a\r\x1c\x00r\ue242\x1a\xb0\xd4AI\x80\x00\x00\u07d4Ju\xc3\xd4\xfao\u033d]\u0567\x03\xc1Sy\xa1\xe7\x83\u9dc9b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94J\x81\xab\xe4\x98L|k\xefc\u0598 \xe5WC\xc6\x1f \x1c\x8a\x03d\x01\x00N\x9a\xa3G\x00\x00\u07d4J\x82iO\xa2\x9d\x9e!2\x02\xa1\xa2\t(]\xf6\xe7E\xc2\t\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\x83\\%\x82LG\xec\xbf\u01d49\xbf?\\4\x81\xaau\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4J\x91\x802C\x91Y\xbb1[g%\xb6\x83\r\xc86\x97s\x9f\x89\x12\xa3.\xf6x3L\x00\x00\u07d4J\x97\xe8\xfc\xf4c^\xa7\xfc^\x96\xeeQu.\u00c8qk`\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4J\x9a&\xfd\n\x8b\xa1\x0f\x97}\xa4\xf7|1\x90\x8d\xabJ\x80\x16\x89a\t=|,m8\x00\x00\u07d4J\xa1H\xc2\xc34\x01\xe6j+Xnew\u0132\x92\xd3\xf2@\x89\v\xb8`\xb2\x85\xf7t\x00\x00\u07d4J\xa6\x93\xb1\"\xf3\x14H*G\xb1\x1c\xc7|h\xa4\x97\x87ab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xb2\xd3O\x04\x83O\xbftyd\x9c\xab\x92=,G%\xc5S\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4J\xc0vs\xe4/d\xc1\xa2^\xc2\xfa-\x86\xe5\xaa+4\xe09\x89lk\x93[\x8b\xbd@\x00\x00\u07d4J\u016c\xad\x00\v\x88w!L\xb1\xae\x00\xea\u0263}Y\xa0\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4J\u0250ZL\xb6\xab\x1c\xfdbTn\xe5\x91s\x00\xb8|O\u07897\b\xba\xed=h\x90\x00\x00\u07d4J\u03e9\xd9N\xdaf%\xc9\u07e5\xf9\xf4\xf5\xd1\a\xc4\x03\x1f\u07c9\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4J\xd0G\xfa\xe6~\xf1b\xfeh\xfe\xdb\xc2};e\xca\xf1\f6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4J\xd9]\x18\x8dddp\x9a\xdd%U\xfbM\x97\xfe\x1e\xbf1\x1f\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\u07d4J\xdb\xf4\xaa\xe0\xe3\xefD\xf7\xddM\x89\x85\u03ef\tn\u010e\x98\x89\b!\xab\rD\x14\x98\x00\x00\u07d4J\xe2\xa0M9\t\xefENTL\xcf\xd6\x14\xbf\xef\xa7\x10\x89\xae\x89\x18\x01\x15\x9d\xf1\xee\xf8\x00\x00\xe0\x94J\xe90\x82\xe4Q\x87\xc2a`\xe6g\x92\xf5\u007f\xad5Q\xc7:\x8a\x04\x96\x15 \xda\xff\x82(\x00\x00\u07d4J\xf0\xdb\a{\xb9\xba^D>!\xe1H\xe5\x9f7\x91\x05\u0152\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x06\x19\xd9\u062a1:\x951\xac}\xbe\x04\xca\rjZ\u0476\x89lk\x93[\x8b\xbd@\x00\x00\u07d4K\v\u062c\xfc\xbcS\xa6\x01\v@\xd4\u040d\xdd-\x9dib-\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\x19\xeb\f5K\xc199`\xeb\x06\x06;\x83\x92o\rg\xb2\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4K)C|\x97\xb4\xa8D\xbeq\u0323\xb6H\xd4\xca\x0f\u075b\xa4\x89\b$q\x984\u03ec\x00\x00\u07d4K1\xbfA\xab\xc7\\\x9a\xe2\u034f\u007f5\x16;n+tPT\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4K:|\u00e7\u05f0\x0e\xd5(\"!\xa6\x02Y\xf2[\xf6S\x8a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K:\xab3^\xbb\xfa\xa8p\xccM`^}.t\xc6h6\x9f\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4K<s\x88\xccv\xda=b\xd4\x00g\u06bc\xcd~\xf0C=#\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4K=\xfb\xdbEK\xe5'\x9a;\x8a\xdd\xfd\x0e\xd1\xcd7\xa9B\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4KG\x0f{\xa00\xbc|\xfc\xf38\u053f\x042\xa9\x1e.\xa5\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4KS\xaeY\u01c4\xb6\xb5\xc46\x16\xb9\xa0\x80\x95X\xe6\x84\xe1\f\x89A\rXj \xa4\xc0\x00\x00\u07d4KX\x10\x1fD\xf7\xe3\x89\xe1-G\x1d\x165\xb7\x16\x14\xfd\xd6\x05\x89\b\xacr0H\x9e\x80\x00\x00\u07d4K\\\xdb\x1eB\x8c\x91\xdd|\xb5Jj\xedEq\xda\x05K\xfeR\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4K`\xa3\xe2S\xbf8\xc8\xd5f \x10\xbb\x93\xa4s\xc9e\xc3\xe5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Kt\xf5\xe5\x8e.\xdfv\xda\xf7\x01Q\x96J\v\x8f\x1d\xe0f<\x89\x11\x90\xaeID\xba\x12\x00\x00\u07d4Kv!f\xdd\x11\x18\xe8Ci\xf8\x04\xc7_\x9c\xd6W\xbfs\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Ky.)h>\xb5\x86\u353b3Rl`\x01\xb3\x97\x99\x9e\x89 \x86\xac5\x10R`\x00\x00\u07d4K\x90N\x93K\xd0\u030b p_\x87\x9e\x90[\x93\xea\f\xcc0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94K\x92\x06\xbakT\x9a\x1a\u007f\x96\x9e\x1d]\xba\x86u9\xd1\xfag\x8a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4K\x98N\xf2lWn\x81Z.\xae\xd2\xf5\x17\u007f\a\u06f1\xc4v\x89T\x91YV\xc4\t`\x00\x00\u07d4K\x9e\x06\x8f\xc4h\tv\xe6\x15\x04\x91)\x85\xfd\\\xe9K\xab\r\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4K\xa0\xd9\xe8\x96\x01w+IhG\xa2\xbbC@\x18g\x87\xd2e\x8965\u026d\xc5\u07a0\x00\x00\u07d4K\xa5:\xb5I\xe2\x01m\xfa\"<\x9e\u0563\x8f\xad\x91(\x8d\a\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94K\xa8\xe0\x11\u007f\xc0\xb6\xa3\xe5k$\xa3\xa5\x8f\xe6\xce\xf4B\xff\x98\x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4K\xac\x84j\xf4\x16\x9f\x1d\x95C\x1b4\x1d\x88\x00\xb2!\x80\xaf\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4K\xb6\xd8k\x83\x14\xc2-\x8d7\xeaQm\x00\x19\xf1V\xaa\xe1-\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94K\xb9e\\\xfb*6\xea|cz{\x85\x9bJ1T\xe2n\xbe\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\xe0\x94K\xbc\xbf8\xb3\xc9\x01c\xa8K\x1c\u04a9;X\xb2\xa34\x8d\x87\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94K\xd6\xdd\f\xff#@\x0e\x170\xba{\x89E\x04W}\x14\xe7J\x8a+\xa0\xcc\xdd\xd0\xdfs\xb0\x00\x00\u07d4K\xe8b\x8a\x81T\x87N\x04\x8d\x80\xc1B\x18\x10\"\xb1\x80\xbc\xc1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4K\xe9\rA!)\u0564\xd0BCa\xd6d\x9dNG\xa6#\x16\x897\b\xba\xed=h\x90\x00\x00\xe0\x94K\xea(\x8e\xeaB\u0115^\xb9\xfa\xad*\x9f\xafG\x83\xcb\u076c\x8a\x06\x18\xbe\x16c\u012fI\x00\x00\u07d4K\xf4G\x97\x99\xef\x82\xee\xa2\tC7OV\xa1\xbfT\x00\x1e^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4K\xf8\xbf\x1d5\xa211Wd\xfc\x80\x01\x80\x9a\x94\x92\x94\xfcI\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4K\xf8\xe2oL'\x90\xdae3\xa2\xac\x9a\xba\xc3\u019a\x19\x943\x89\n\u05ce\xbcZ\xc6 \x00\x00\u0794L\n\xcaP\x8b<\xaf^\xe0(\xbcp}\xd1\xe8\x00\xb88\xf4S\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94L\v\x15\x15\xdf\xce\u05e1>\x13\xee\x12\xc0\xf5#\xaePO\x03+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4L\x13\x98\f2\xdc\xf3\x92\vx\xa4\xa7\x903\x12\x90|\x1b\x12?\x89\x03A\x00\x15\xfa\xae\f\x00\x00\u07d4L\x15y\xaf3\x12\xe4\xf8\x8a\xe9<h\xe9D\x9c.\x9ah\xd9\u0109lk\x93[\x8b\xbd@\x00\x00\xe0\x94L#\xb3p\xfc\x99+\xb6|\xec\x06\xe2g\x15\xb6/\v:J\u00ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4L$\xb7\x8b\xaf+\xaf\xc7\xfc\u0190\x16Bk\xe9s\xe2\nP\xb2\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94L/\x1a\xfe\xf7\u0146\x8cD\x83/\xc7|\xb0;U\xf8\x9emn\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4L7{\xb0:\xb5,L\xb7\x9b\xef\xa1\xdd\x11I\x82\x92LJ\xe9\x89c\x16\x03\xcc\u04cd\xd7\x00\x00\u07d4L>\x95\xcc9W\xd2R\xce\v\xf0\xc8}[O\"4g.p\x89\x87\x86x2n\xac\x90\x00\x00\u07d4LB<v\x93\r\a\xf9<G\xa5\xccOaWE\xc4Z\x9dr\x89\x05k\xc7^-c\x10\x00\x00\u07d4LE\xd4\u0267%\xd1\x11\x12\xbf\xcb\xca\x00\xbf1\x18l\u02ad\xb7\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4LNo\x13\xfb^?p\xc3v\x02b\xa0>1y\x82i\x1d\x10\x89\x05k\xc7^-c\x10\x00\x00\u07d4LZ\xfe@\xf1\x8f\xfcH\u04e1\xae\xc4\x1f\u009d\xe1y\xf4\u0497\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L[=\xc0\xe2\xb96\x0f\x91(\x9b\x1f\xe1<\xe1,\x0f\xbd\xa3\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lfk\x86\xf1\xc5\ue324\x12\x85\xf5\xbd\xe4\xf7\x90R\b\x14\x06\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Lik\xe9\x9f:i\x04@\xc3CjY\xa7\xd7\xe97\u05ba\r\x89\xbb\x91%T\"c\x90\x00\x00\u07d4Lj$\x8f\xc9}p]\xefI\\\xa2\aY\x16\x9e\xf0\xd3dq\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4Lj\x9d\xc2\u02b1\n\xbb.|\x13p\x06\xf0\x8f\ucd77y\xe1\x89\x1b\r\x04 /G\xec\x00\x00\u07d4Lk\x93\xa3\xbe\xc1cIT\f\xbf\xca\xe9l\x96!\xd6dP\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Lu\x98\x13\xad\x13\x86\xbe\xd2\u007f\xfa\xe9\xe4\x81^60\u0323\x12\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Lv\f\xd9\xe1\x95\xeeO-k\xce%\x00\xff\x96\xda|C\ue44a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4Lv{e\xfd\x91\x16\x1fO\xbd\xccji\xe2\xf6\xadq\x1b\xb9\x18\x89'\b\x01\xd9F\xc9@\x00\x00\u07d4L~.+w\xad\f\xd6\xf4J\xcb(a\xf0\xfb\x8b(u\x0e\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4L\x85\xed6/$\xf6\xb9\xf0L\xdf\xcc\xd0\"\xaeSQG\u02f9\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4L\x93[\xb2Pw\x8b<L\u007f~\a\xfc%\x1f\xa601J\xab\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4L\x99y\x92\x03l[C:\xc3=%\xa8\xea\x1d\xc3\xd4\xe4\xe6\u0609\x01\x95;=J\xb1h\x00\x00\u07d4L\x99\xda\xe9d\x81\xe8\a\xc1\xf9\x9f\x8b\u007f\xbd\xe2\x9buG\u017f\x89\b!\xab\rD\x14\x98\x00\x00\u07d4L\x9a\x86*\xd1\x15\xd6\xc8'N\u0439D\xbd\u05a5P\x05\x10\xa7\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94L\xa7\x83\xb5V\xe5\xbfS\xaa\x13\xc8\x11f\x13\xd6W\x82\u0276B\x8a\x05V\x18@\xb4\xad\x83\xc0\x00\x00\u07d4L\xa7\xb7\x17\u067c\x87\x93\xb0N\x05\x1a\x8d#\xe1d\x0f[\xa5\xe3\x89C\xb5\x14T\x9e\xcfb\x00\x00\u07d4L\xa8\xdbJ^\xfe\xfc\x80\xf4\u035b\xbc\u03302e\x93\x132\xb6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4L\xac\x91\xfb\x83\xa1G\xd2\xf7l2g\x98K\x91\ny\x933H\x89uy*\x8a\xbd\xef|\x00\x00\u07d4L\xad\xf5s\xceL\xee\u01cb\x8e\x1b!\xb0\xedx\xeb\x11;,\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L\xb5\xc6\xcdq<\xa4G\xb8H\xae/V\xb7a\xca\x14\u05edW\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4L\xc2,\x9b\u026d\x05\xd8u\xa3\x97\xdb\xe8G\xed\"\x1c\x92\fg\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L\u0430\xa6CcbY\\\xea\xde\x05.\xbc\x9b\x92\x9f\xb6\xc6\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4L\xdaA\xddS9\x91)\a\x94\xe2*\xe3$\x14>0\x9b==\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4L\xee\x90\x1bJ\u0231V\xc5\xe2\xf8\xa6\xf1\xbe\xf5r\xa7\xdc\xeb~\x8965\u026d\xc5\u07a0\x00\x00\u07d4L\xef\xbe#\x98\xe4}R\u73743L\x8bivu\U00053b89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4L\xf5S{\x85\x84/\x89\xcf\xee5\x9e\xaeP\x0f\xc4I\xd2\x11\x8f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94M\bG\x1dh\x00z\xff*\xe2y\xbc^?\xe4\x15o\xbb\xe3\u078a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4M \x01\x10\x12@\b\xd5ov\x98\x12VB\f\x94jo\xf4\\\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4M$\xb7\xacG\xd2\xf2}\xe9\tt\xba=\xe5\xea\xd2\x03TK\u0349\x05k\xc7^-c\x10\x00\x00\u0794M)\xfcR:,\x16)S!!\u0699\x98\u9d6b\x9d\x1bE\x88\xdbD\xe0I\xbb,\x00\x00\u07d4M8\xd9\x0f\x83\xf4Q\\\x03\xccx2j\x15M5\x8b\u0602\xb7\x89\n\ad\a\xd3\xf7D\x00\x00\u07d4ML\xf5\x80t)a^0\xcd\xfa\xce\x1eZ\xaeM\xad0U\xe6\x89 \x86\xac5\x10R`\x00\x00\u07d4MW\xe7\x16\x87l\f\x95\xef^\xae\xbd5\xc8\xf4\x1b\x06\x9bk\xfe\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Mg\U000ab159\xfe\xf5\xfcA9\x99\xaa\x01\xfd\u007f\xcep\xb4=\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Mn\x8f\xe1\t\xcc\xd2\x15\x8eM\xb1\x14\x13/\xe7_\xec\u023e[\x89\x01[5W\xf1\x93\u007f\x80\x00\xe0\x94Mq\xa6\xeb=\u007f2~\x184'\x8e(\v\x03\x9e\xdd\xd3\x1c/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4M|\xfa\xa8L\xb31\x06\x80\n\x8c\x80/\xb8\xaaF8\x96\u0159\x89a\t=|,m8\x00\x00\u07d4M\x80\x10\x93\xc1\x9c\xa9\xb8\xf3B\xe3<\xc9\xc7{\xbdL\x83\x12\u03c9\x12\xb3\xe7\xfb\x95\u0364\x80\x00\u07d4M\x82\x88\x94u/o%\x17]\xaf!w\tD\x87\x95Ko\x9f\x89O!+\xc2\u011c\x83\x80\x00\xe0\x94M\x82\xd7p\f\x12;\xb9\x19A\x9b\xba\xf0Fy\x9ck\x0e,f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4M\x83m\x9d;\x0e,\xbdM\xe0PYo\xaaI\f\xff\xb6\r]\x89\x10CV\x1a\x88)0\x00\x00\u07d4M\x86\x97\xaf\x0f\xbf,\xa3n\x87h\xf4\xaf\"\x135phZ`\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\x92y\x96 )\xa8\xbdEc\x977\xe9\x8bQ\x1e\xff\aL!\x89Hz\x9a0E9D\x00\x00\u07d4M\x93io\xa2HY\xf5\u0493\x9a\xeb\xfaT\xb4\xb5\x1a\xe1\xdc\u0309\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4M\x9cw\xd0u\f^o\xbc$\u007f/\u05d2thl\xb3S\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4M\xa5\xed\u0188\xb0\xcbb\xe1@=\x17\x00\xd9\u0739\x9f\xfe?\u04c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xa8\x03\ai\x84K\xc3A\x86\xb8\\\xd4\xc74\x88I\xffI\xe9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xb1\xc4:\x0f\x83M}\x04x\xb8\x96\ag\xec\x1a\xc4L\x9a\xeb\x89/Q\x810V'7\x00\x00\u07d4M\xb2\x12\x84\xbc\xd4\xf7\x87\xa7Ue\x00\xd6\xd7\xd8\xf3f#\xcf5\x89i(7Ow\xa3c\x00\x00\u07d4M\xc3\xda\x13\xb2\xb4\xaf\xd4O]\r1\x89\xf4D\xd4\xdd\xf9\x1b\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4M\u013f^u\x89\xc4{(7\x8du\x03\u03d6H\x80a\u06fd\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4M\xc9\u057bK\x19\xce\u0354\xf1\x9e\xc2] \x0e\xa7/%\xd7\xed\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94M\xcd\x11\x81X\x18\xae)\xb8]\x016sI\xa8\xa7\xfb\x12\xd0k\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\u07d4M\xcfb\xa3\xde?\x06\x1d\xb9\x14\x98\xfda\x06\x0f\x1fc\x98\xffs\x89lj\xccg\u05f1\xd4\x00\x00\u07d4M\xd11\xc7J\x06\x8a7\xc9\n\xde\xd4\xf3\t\xc2@\x9fdx\u04c9\x15\xaf9\u4ab2t\x00\x00\xe0\x94M\u0767Xk\"7\xb0S\xa7\xf3(\x9c\xf4`\xdcW\xd3z\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4M\xe3\xfe4\xa6\xfb\xf64\xc0Q\x99\u007fG\xcc\u007fHy\x1fX$\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4M\xf1@\xbaye\x85\xddT\x891[\xcaK\xbah\n\u06f8\x18\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4N\x02\ay\xb5\xdd\xd3\xdf\"\x8a\x00\xcbH\xc2\xfc\x97\x9d\xa6\xae8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N\v\xd3$s\xc4\xc5\x1b\xf2VT\xde\xf6\x9fy|k)\xa22\x89V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4N\"%\xa1\xbbY\xbc\x88\xa21ft\xd33\xb9\xb0\xaf\xcafU\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4N#\x10\x19\x1e\xad\x8d;\xc6H\x98s\xa5\xf0\xc2\xeck\x87\u1f8965\u026d\xc5\u07a0\x00\x00\u07d4N#-S\xb3\u6f8f\x89Sa\xd3\x1c4\xd4v+\x12\xc8.\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4N+\xfaJFo\x82g\x1b\x80\x0e\xeeBj\xd0\f\a\x1b\xa1p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4N>\xda\u0506M\xabd\xca\xe4\xc5Azvw@S\xdcd2\x89 \b\xfbG\x8c\xbf\xa9\x80\x00\u07d4NC\x18\xf5\xe1>\x82JT\xed\xfe0\xa7\xedO&\xcd=\xa5\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4N[w\xf9\x06aY\xe6\x15\x93?-\xdatw\xfaNG\xd6H\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94Nf\x00\x80b\x89EJ\u03630\xa2\xa3U`\x10\u07ec\xad\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Ns\xcf#y\xf1$\x86\x0fs\xd6\xd9\x1b\xf5\x9a\xcc\\\xfc\x84[\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94Nz\xa6~\x12\x18>\xf9\xd7F\x8e\xa2\x8a\xd29\xc2\xee\xf7\x1bv\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94N{TGM\x01\xfe\xfd8\x8d\xfc\xd5;\x9ff&$A\x8a\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94N\x89.\x80\x81\xbf6\xe4\x88\xfd\xdb;&0\xf3\xf1\xe8\xda0\u048a\x02\x8a\xba0u$Q\xfc\x00\x00\xe0\x94N\x8amcH\x9c\xcc\x10\xa5\u007f\x88_\x96\xeb\x04\xec\xbbT`$\x8a\x03\xea\xe3\x13\x0e\u0316\x90\x00\x00\u07d4N\x8eG\xae;\x1e\xf5\f\x9dT\xa3\x8e\x14 \x8c\x1a\xbd6\x03\u0089y(\xdb\x12vf\f\x00\x00\u0794N\x90\u03312X\xac\xaa\x9fO\xeb\xc0\xa3B\x92\xf9Y\x91\xe20\x88\xdbD\xe0I\xbb,\x00\x00\u07d4N\xa5n\x11\x12d\x1c\x03\x8d\x05e\xa9\u0096\xc4c\xaf\xef\xc1~\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94N\xa7\x0f\x041?\xaee\xc3\xff\"J\x05\\=-\xab(\xdd\u07ca\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4N\xb1EKW8\x05\u022c\xa3~\xde\xc7\x14\x9aA\xf6\x12\x02\xf4\x89\x10CV\x1a\x88)0\x00\x00\u07d4N\xb8{\xa8x\x8e\xba\r\xf8~[\x9b\xd5\n\x8eE6\x80\x91\xc1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4N\xbcV)\xf9\xa6\xa6k,\xf36:\u0109\\\x03H\u8fc7\x8967\tlK\xcci\x00\x00\u07d4N\xc7h)^\xea\xba\xfcB\x95\x84\x15\xe2+\xe2\x16\xcd\xe7v\x18\x89\x03;\x1d\xbc9\xc5H\x00\x00\u07d4N\xcc\x19\x94\x8d\xd9\u0347\xb4\xc7 \x1a\xb4\x8eu\x8f(\xe7\xccv\x89\x1b\x1d\xaba\u04ead\x00\x00\u07d4N\xd1M\x81\xb6\v#\xfb%\x05M\x89%\u07e5s\u072eah\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94N\xe1<\rA \vF\u045d\xee\\K\xce\xc7\x1d\x82\xbb\x8e8\x8a\x01\xab\xee\x13\u033e\ufbc0\x00\u07d4N\xea\xd4\n\xad\x8cs\xef\b\xfc\x84\xbc\n\x92\xc9\t/j6\xbf\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4N\xeb\xe8\f\xb6\xf3\xaeY\x04\xf6\xf4\xb2\x8d\x90\u007f\x90q\x89\xfc\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4N\xeb\xf1 ]\f\xc2\f\xeel\u007f\x8f\xf3\x11_V\u050f\xba&\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4N\xf1\xc2\x14c:\xd9\xc0p;N#t\xa2\xe3>>B\x92\x91\x89Hz\x9a0E9D\x00\x00\u07d4N\xfc\xd9\u01df\xb43L\xa6${\n3\xbd\x9c\xc32\b\xe2r\x89Hz\x9a0E9D\x00\x00\xe0\x94O\x06$k\x8dK\u0496a\xf4>\x93v\"\x01\u0486\x93Z\xb1\x8a\x01\x059O\xfcF6\x11\x00\x00\u07d4O\x15+/\xb8e\x9dCwn\xbb\x1e\x81g:\xa8Ai\xbe\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4O\x17\u007f\x9dV\x95=\xedq\xa5a\x1f93\"\xc3\x02y\x89\\\x89\rU\uf422\xda\x18\x00\x00\u07d4O\x1a-\xa5JLm\xa1\x9d\x14$\x12\xe5n\x81WA\xdb#%\x89\x05k\xc7^-c\x10\x00\x00\u07d4O#\xb6\xb8\x17\xff\xa5\xc6d\xac\xda\u05db\xb7\xb7&\xd3\n\xf0\xf9\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94O&i\f\x99+z1*\xb1.\x13\x85\xd9J\xcdX(\x8e{\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4O+G\xe2wZ\x1f\xa7\x17\x8d\xad\x92\x98Z[\xbeI;\xa6\u0589\n\u05ce\xbcZ\xc6 \x00\x00\u07d4O:HT\x91\x11E\xea\x01\xc6D\x04K\xdb.Z\x96\n\x98/\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4O?,g0i\xac\x97\xc2\x026\a\x15)\x81\xf5\xcd`c\xa0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94OJ\x9b\xe1\f\xd5\xd3\xfb]\xe4\x8c\x17\xbe)o\x89V\x90d[\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4OR\xadap\xd2[*.\x85\x0e\xad\xbbRA?\xf20>\u007f\x89\xa4\xccy\x95c\u00c0\x00\x00\u07d4OX\x01\xb1\xeb0\xb7\x12\u0620WZ\x9aq\xff\x96]O4\xeb\x89\x10CV\x1a\x88)0\x00\x00\u07d4O]\xf5\xb9CW\u0794\x86\x04\xc5\x1bx\x93\xcd\xdf`v\xba\xad\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4Od\xa8^\x8e\x9a@I\x8c\fu\xfc\xeb\x037\xfbI\b>^\x8965\u026d\xc5\u07a0\x00\x00\u07d4Og9m%S\xf9\x98x_pN\a\xa69\x19}\u0454\x8d\x89\x10DrR\x1b\xa78\x00\x00\u07d4OmG7\u05e9@8$\x87&H\x86i|\xf7c\u007f\x80\x15\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4Os0\toy\xed&N\xe0\x12\u007f]0\xd2\xf7<R\xb3\u0609\x1b\x1azB\v\xa0\r\x00\x00\u07d4Ov{\xc8yJ\xef\x9a\n8\xfe\xa5\xc8\x1f\x14iO\xf2\x1a\x13\x89\x1b\xc43\xf2?\x83\x14\x00\x00\u07d4O\x85\xbc\x1f\xc5\xcb\xc9\xc0\x01\xe8\xf17.\aPSp\xd8\xc7\x1f\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4O\x88\xdf\xd0\x10\x91\xa4Z\x9e&v\x02\x1ed(l\xd3k\x8d4\x8965\u026d\xc5\u07a0\x00\x00\u07d4O\x89r\x83\x8fp\xc9\x03\u0276\xc6\xc4ab\xe9\x9db\x16\xd4Q\x89\xf9\xe8\x9a\x0f,V\xc8\x00\x00\u07d4O\x8a\xe8\x028\xe6\x00\bUpu\xabj\xfe\n\u007f.t\xd7)\x89\x05k\xc7^-c\x10\x00\x00\u07d4O\x8e\x8d'O\xb2*?\xd3jG\xfer\x98\x04qTK44\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94O\x9c\u2bdb\x8c^B\u0180\x8a8p\xecWo15E\u044a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4O\xa3\xf3.\xf4\bdH\xb3D\xd5\xf0\xa9\x89\r\x1c\xe4\xd6\x17\u00c9QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4O\xa5T\xab\x95\\$\x92\x178jM2c\xbb\xf7(\x95CN\x89\x01\x15NS!}\xdb\x00\x00\u07d4O\xa9\x83\xbb^0s\xa8\xed\xb5W\xef\xfe\xb4\xf9\xfb\x1d`\uf189V\xb9\xafW\xe5u\xec\x00\x00\u07d4O\xaf\x90\xb7n\u03f9c\x1b\xf9\x02!v\x03-\x8b, p\t\x8966;]\x9awp\x00\x00\u07d4O\xc4l9ngHi\xad\x94\x81c\x8f\x00\x13c\f\x87\u02ac\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94O\xcc\x19\xea\x9fLW\u073c\xe8\x93\x19<\xfb\x16j\xa9\x14\xed\u014a\x01{\x8b\xaa\u007f\x19Tj\x00\x00\xe0\x94O\u0384)\xbaI\u02a06\x9d\x1eIM\xb5~\x89\uacad9\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4O\xda\xc1\xaaQp\a\xe0\b\x940\xb31j\x1b\xad\xd1,\x01\u01c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4O\xe5j\xb3\xba\u1c24D3E\x833\u0130Z$\x8f\x82A\x89v-\x93\xd1\xddo\x90\x00\x00\u07d4O\xeb\x84k\xe40A\xfdk4 (\x97\x94>?!\xcb\u007f\x04\x89\x04\x82\xfe&\f\xbc\xa9\x00\x00\u07d4O\xeeP\xc5\xf9\x88 k\t\xa5sF\x9f\xb1\u0434.\xbbm\u0389l\xee\x06\u077e\x15\xec\x00\x00\u07d4O\xf6v\xe2\u007fh\x1a\x98-\x8f\xd9\xd2\x0ed\x8b=\xce\x05\xe9E\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4O\xf6\u007f\xb8\u007fn\xfb\xa9'\x990\u03fd\x1bz4<y\xfa\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4P\x06\xfeL\"\x179\x80\xf0\ft4+9\xcd#\x1ce1)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\f\x165.\x90\x1dH\xba\x8d\x04\xe2\xc7g\x12\x17ry\v\x02\x89\x01\xa3\xa6\x82Is\t\x80\x00\u07d4P\f\x90)X\xf6B\x15\x94\u0476\xde\xd7\x12I\rR\xedlD\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94P\x0e4\xcd\u5f5e+q\xbb\x92\xd7\xcfU\xee\xe1\x88\xd5\xfa\f\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4P2\xe4\xbc\xf7\x93+I\xfd\xba7{o\x14\x99ce\x13\xcf\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4P7\x8a\xf7\xefT\x04?\x89*\xb7\u0397\xd6Gy5\x11\xb1\b\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4P;\xdb\u063cB\x1c2\xa4C\x03-\xeb.>L\u057a\x8bN\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94PFf\u03891\x17^\x11\xa5\xed\x11\xc1\u072a\x06\xe5\u007fNf\x8a\x02\u007f>\u07f3Nn@\x00\x00\u0794PXM\x92\x06\xa4l\xe1\\0\x11\x17\xee(\xf1\\0\xe6\x0eu\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94PZ3\xa1\x864\xddH\x00i<g\xf4\x8a\x1di=H3\xf8\x8a\x01\x89!\xb7\x99A\xdc\xd0\x00\x00\u0794P^O|'U\x88\xc53\xa2\x0e\xbd*\xc1;@\x9b\xbd\xea<\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4Pb\xe5\x13La/\x12iM\xbd\x0e\x13\x1dL\xe1\x97\u0476\xa4\x8965\u026d\xc5\u07a0\x00\x00\u07d4Pd\x11\xfdy\x004\x80\xf6\xf2\xb6\xaa\xc2k{\xa7\x92\U00014c89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Pg\xf4T\x9a\xfb\xfe\x88LY\xcb\xc1+\x96\x93I#\xd4]\xb0\x8965\u026d\xc5\u07a0\x00\x00\u07d4Pv:\u0746\x8f\xd76\x11x4/\xc0U\ua8b9_hF\x89\x03\x9f\x90F\xe0\x89\x8f\x00\x00\u07d4P\x8c\xf1\x91\x19\xdbp\xaa\x86EBS\xdavJ,\xb1\xb2\xbe\x1a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94P\x99\x82\xf5b7\xeeE\x89Q\x04~\n\"0\xf8\x04\xe2\u854a\x03\xb4\xadIa\x06\xb7\xf0\x00\x00\u07d4P\x9a \xbcH\xe7+\xe1\u036f\x95i\xc7\x11\xe8d\x8d\x95s4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\x9c\x86h\x03m\x14?\xb8\xaep\xb1\x19\x95c\x1f=\xfc\xad\x87\x8965\u026d\xc5\u07a0\x00\x00\u07d4P\xad\x18z\xb2\x11g\u00b6\xe7\x8b\xe0\x15?DPJ\a\x94^\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4P\xb9\xfe\xf0\xa12\x9b\x02\xd1e\x06%_Z-\xb7\x1e\xc9-\x1f\x89G\u0682\x15d\b\\\x00\x00\u07d4P\xbbg\u0238\u063d\x0fc\xc4v\t\x04\xf2\xd33\xf4\x00\xaa\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4P\xbe\xf2ubH\xf9\xa7\xa3\x80\xf9\x1b\x05\x1b\xa3\xbe(\xa6I\xed\x89li\xf7>)\x13N\x00\x00\u07d4P\u0286\xb5\xeb\x1d\x01\x87M\xf8\xe5\xf3IE\u051cl\x1a\xb8H\x8965\u026d\xc5\u07a0\x00\x00\u07d4P\u0357\xe97\x8b\\\xf1\x8f\x179c#l\x99Q\xeft8\xa5\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4P\u073c'\xbc\xad\x98@\x93\xa2\x12\xa9\xb4\x17\x8e\xab\xe9\x01ua\x89\a\xe3by\v\\\xa4\x00\x00\u07d4P\xe10#\xbd\x9c\xa9j\xd4\xc5?\xdf\xd4\x10\xcbk\x1fB\v\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94P\xe1\xc8\xec\x98A[\xefD&\x18p\x87\x99C{\x86\xe6\xc2\x05\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4P\xf8\xfaK\xb9\xe2g|\x99\nN\xe8\xcep\xdd\x15#%\x1eO\x89\x01i=#\x16Ok\x00\x00\u07d4P\xfb6\xc2q\a\xee,\xa9\xa3#n'F\u0321\x9a\xcekI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4P\xfe\xf2\x96\x95U\x88\u02aet\xc6.\xc3*#\xa4T\xe0\x9a\xb8\x89A\x1d\xff\xab\xc5\a8\x00\x00\u07d4Q\x02\xa4\xa4 w\xe1\x1cX\xdfGs\u3b14F#\xa6m\x9f\x89lp\x15\xfdR\xed@\x80\x00\u07d4Q\x03\x93w\xee\xd0\xc5s\xf9\x86\xc5\xe8\xa9_\xb9\x9aY\xe93\x0f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Q\x03\xbc\t\x93>\x99!\xfdS\xdcSo\x11\xf0]\rG\x10}\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94Q\x04\xec\xc0\xe30\xdd\x1f\x81\xb5\x8a\xc9\u06f1\xa9\xfb\xf8\x8a<\x85\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4Q\r\x81Y\u0314Wh\xc7E\a\x90\xba\a>\xc0\xd9\xf8\x9e0\x89\x8a\xc7#\x04\x89\xe8\x00\x00\x00\u07d4Q\x0e\xdaV\x01I\x9a\r^\x1a\x00k\xff\xfd\x836r\xf2\xe2g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x12dF\xab=\x802U~\x8e\xbaeY}u\xfa\u0701\\\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94Q\x18U}`\r\x05\xc2\xfc\xbf8\x06\xff\xbd\x93\xd0 %\xd70\x8a\x02g\u04ebd#\xf5\x80\x00\x00\u07d4Q\x1e\x0e\xfb\x04\xacN?\xf2\xe6U\x0eI\x82\x95\xbf\xcdV\xff\u0549$=M\x18\"\x9c\xa2\x00\x00\u07d4Q!\x16\x81{\xa9\xaa\xf8C\xd1P|e\xa5\xead\n{\x9e\xec\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4Q&F\ri,q\u026fo\x05WM\x93\x99\x83h\xa27\x99\x89\x02\u0465\x1c~\x00P\x00\x00\u07d4Q'\u007f\xe7\xc8\x1e\xeb\xd2R\xa0=\xf6\x9ak\x9f2n'\"\a\x89\x03@.y\u02b4L\x80\x00\u07d4Q)oPD'\r\x17pvF\x12\x9c\x86\xaa\xd1d^\xad\xc1\x89H|r\xb3\x10\xd4d\x80\x00\xe0\x94Q+\x91\xbb\xfa\xa9\xe5\x81\xefh?\xc9\r\x9d\xb2*\x8fI\xf4\x8b\x8aA\xa5\"8m\x9b\x95\xc0\x00\x00\u07d4Q5\xfb\x87W`\f\xf4tTbR\xf7M\xc0tm\x06&,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4QF2\xef\xbdd,\x04\xdel\xa3B1]@\u0750\xa2\u06e6\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4QKu\x12\u026e^\xa6<\xbf\x11q[c\xf2\x1e\x18\u0496\xc1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4QS\xa0\xc3\u0211(\x81\xbf\x1c5\x01\xbfd\xb4VI\xe4\x82\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94QVQ\xd6\xdbO\xaf\x9e\xcd\x10:\x92\x1b\xbb\xbej\xe9p\xfd\u050a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94Q_0\xbc\x90\xcd\xf4W~\xe4}e\u05c5\xfb\xe2\xe87\u01bc\x8a\x02'\x1b^\x01\x8b\xa0X\x00\x00\u07d4Q`\xeda.\x1bH\xe7??\xc1[\xc42\x1b\x8f#\xb8\xa2K\x89\x1e\x82kB(e\xd8\x00\x00\u07d4Qa\xfdI\xe8G\xf6tU\xf1\u023bz\xbb6\xe9\x85&\r\x03\x89A\rXj \xa4\xc0\x00\x00\u07d4QiT\x02_\xca&\b\xf4}\xa8\x1c!^\xed\xfd\x84J\t\xff\x89\x14\xb5P\xa0\x13\xc78\x00\x00\u07d4Qi\xc6\n\xeeL\xee\u0444\x9a\xb3mfL\xff\x97\x06\x1e\x8e\xa8\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4Q|uC\r\xe4\x01\xc3A\x03&\x86\x11'\x90\xf4mM6\x9e\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4Q|\xd7`\x8e]\r\x83\xa2kq\u007f6\x03\xda\xc2'}\u00e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x86]\xb1H\x88\x19Q\xf5\x12Qq\x0e\x82\xb9\xbe\r~\xad\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\x89\x1b,\xcd\xd2\xf5\xa4K*\x8b\u011a]\x9b\xcadw%\x1c\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4Q\x8c\xef'\xb1\x05\x82\xb6\xd1OiH=\u06a0\xdd<\x87\xbb\\\x89 \x86\xac5\x10R`\x00\x00\u07d4Q\xa6\xd6'\xf6j\x89#\u060d`\x94\xc4qS\x80\xd3\x05|\xb6\x89>s\xd2z5\x94\x1e\x00\x00\u07d4Q\xa8\xc2\x166\x02\xa3.\xe2L\xf4\xaa\x97\xfd\x9e\xa4\x14QiA\x89\x03h\xf7\xe6\xb8g,\x00\x00\u07d4Q\xb4u\x8e\x9e\x14P\xe7\xafBh\xc3\u01f1\xe7\xbdo\\uP\x8965\u026d\xc5\u07a0\x00\x00\u07d4Q\u028b\xd4\xdcdO\xacG\xafgUc\u0540J\r\xa2\x1e\xeb\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4Q\xd2K\xc3so\x88\xddc\xb7\" &\x88f0\xb6\ub1cd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Q\u05cb\x17\x8dp~9n\x87\x10\x96\\OA\xb1\xa1\xd9\x17\x9d\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\u07d4Q\xe3/\x14\xf4\xca^(|\xda\xc0W\xa7y^\xa9\xe0C\x99S\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Q\xe4?\xe0\xd2\\x(`\xaf\x81\xea\x89\xddy<\x13\xf0\u02f1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4Q\xe7\xb5\\/\x98 \xee\xd78\x846\x1bPf\xa5\x9boE\u0189lk\x93[\x8b\xbd@\x00\x00\xe0\x94Q\xea\x1c\t4\xe3\xd0@\"\ud715\xa0\x87\xa1P\xefp^\x81\x8a\x01Tp\x81\xe7\"M \x00\x00\u07d4Q\xee\f\xca;\xcb\x10\xcd>\x987\"\xce\xd8I=\x92l\bf\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94Q\xf4f:\xb4O\xf7\x93E\xf4'\xa0\xf6\xf8\xa6\u0225?\xf24\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Q\xf5^\xf4~dV\xa4\x18\xab2\xb9\"\x1e\xd2}\xbaf\b\xee\x89\u3bb5sr@\xa0\x00\x00\xe0\x94Q\xf9\xc42\xa4\xe5\x9a\xc8b\x82\u05ad\xabL.\xb8\x91\x91`\xeb\x8ap;[\x89\u00e6\xe7@\x00\x00\u07d4R\x0ff\xa0\xe2e\u007f\xf0\xacA\x95\xf2\xf0d\xcf/\xa4\xb2BP\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4R\x10#T\xa6\xac\xa9]\x8a.\x86\xd5\u07bd\xa6\xdei4`v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\x13\xf4Y\xe0x\xad:\xb9Z\t #\x9f\xcf\x163\xdc\x04\u0289\x8c\xf2\x18|*\xfb\x18\x80\x00\u07d4R\x15\x18;\x8f\x80\xa9\xbc\x03\xd2l\xe9\x12\a\x83*\r9\xe6 \x8965\u026d\xc5\u07a0\x00\x00\xe0\x94R!Cx\xb5@\x04\x05j|\xc0\x8c\x89\x13'y\x8a\u01b2H\x8a\x037\xfe_\xea\xf2\u0440\x00\x00\xe0\x94R##\xaa\xd7\x1d\xbc\x96\xd8Z\xf9\x0f\bK\x99\xc3\xf0\x9d\ucdca\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4R>\x14\r\xc8\x11\xb1\x86\xde\xe5\xd6\u020b\xf6\x8e\x90\xb8\xe0\x96\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R?mdi\x0f\xda\u0354(SY\x1b\xb0\xff \xd3em\x95\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4RO\xb2\x10R,^#\xbbg\u07ff\x8c&\xaaam\xa4\x99U\x8965b\xa6m4#\x80\x00\u07d4RU\xdci\x15ZE\xb9p\xc6\x04\xd3\x00G\xe2\xf50i\x0e\u007f\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R`\xdcQ\xee\a\xbd\u06ab\xab\xb9\xeetK9<\u007fG\x93\xa6\x89\x01\xd8f_\xa5\xfaL\x00\x00\u07d4Rg\xf4\xd4\x12\x92\xf3p\x86<\x90\u05d3)i\x03\x846%\u01c9K\xe4\xe7&{j\xe0\x00\x00\u07d4Rk\xb53\xb7n \xc8\xee\x1e\xbf\x12?\x1e\x9f\xf4\x14\x8e@\xbe\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Rl\xb0\x9c\u3b63g.\xec\x1d\xebF [\xe8\x9aKV>\x89\x85\xcaa[\xf9\xc0\x10\x00\x00\u07d4Rs\x8c\x90\xd8`\xe0L\xb1/I\x8d\x96\xfd\xb5\xbf6\xfc4\x0e\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4Rz\x8c\xa1&\x863\xa6\xc99\xc5\xde\x1b\x92\x9a\ue4ae\xac\x8d\x890\xca\x02O\x98{\x90\x00\x00\u07d4R\x81\x01\xceF\xb7 \xa2!M\u036ef\x18\xa51w\xff\xa3w\x89\x1b\x96\x12\xb9\xdc\x01\xae\x00\x00\xe0\x94R\x81s4s\xe0\r\x87\xf1\x1e\x99U\u5275\x9fJ\u008ez\x8a\x8b\xd6/\xf4\xee\xc5Y \x00\x00\u07d4R\x98\xab\x18*\x195\x9f\xfc\xec\xaf\xd7\u0475\xfa!-\xed\xe6\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4R\x9a\xa0\x02\u0196*:\x85E\x02\u007f\u0630_\"\xb5\xbf\x95d\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4R\x9e\x82O\xa0rX+@2h:\xc7\xee\xcc\x1c\x04\xb4\xca\xc1\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94R\xa5\xe4\xdeC\x93\xee\xcc\xf0X\x1a\xc1\x1bR\u0183\xc7n\xa1]\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4R\xb4%|\xf4\x1bn(\x87\x8dP\xd5{\x99\x91O\xfa\x89\x87:\x89\xd5\r\u026a,Aw\x00\x00\u07d4R\xb8\xa9Y&4\xf70\v|\\Y\xa34[\x83_\x01\xb9\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xbd\u066fYx\x85\v\xc2A\x10q\x8b7#u\x9bC~Y\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4R\xcd @;\xa7\xed\xa6\xbc0z=c\xb5\x91\x1b\x81|\x12c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794R\u04c0Q\x1d\xf1\x9d^\u0080{\xbc\xb6vX\x1bg\xfd7\xa3\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94R\xe1s\x13P\xf9\x83\xcc,A\x89\x84/\xde\x06\x13\xfa\xd5\f\xe1\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4R\xe4g\x832\x9av\x93\x01\xb1u\x00\x9d4gh\xf4\xc8~\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4R\xf0X\xd4aG\xe9\x00m)\xbf,\t0J\xd1\xcd\xddn\x15\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4R\xf1T#2<$\xf1\x9a\xe2\xabg7\x17\"\x9d?t}\x9b\x897\xa04\xcb\xe8\xe3\xf3\x80\x00\u07d4R\xf8\xb5\t\xfe\xe1\xa8t\xabo\x9d\x876\u007f\xbe\xaf\x15\xac\x13\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4R\xfbF\xac]\x00\xc3Q\x8b,:\x1c\x17}D/\x81eU_\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4S\x00w\xc9\xf7\xb9\a\xff\x9c\xec\fw\xa4\x1ap\xe9\x02\x9a\xddJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x03\x19\xdb\n\x8f\x93\xe5\xbb}M\xbfH\x161O\xbe\xd86\x1b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\x04}\u022c\x90\x83\xd9\x06r\xe8\xb3G<\x10\f\xcd'\x83#\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4S\va\xe4/9Bm$\b\xd4\bR\xb9\xe3J\xb5\xeb\xeb\u0149\x0e~\xeb\xa3A\vt\x00\x00\u07d4S\x0f\xfa\u00fc4\x12\xe2\xec\x0e\xa4{y\x81\xc7p\xf5\xbb/5\x89\a?u\u0460\x85\xba\x00\x00\u07d4S\x17\xec\xb0#\x05,\xa7\xf5e+\xe2\xfa\x85L\xfeEc\xdfM\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4S\x19M\x8a\xfa>\x885\x02v~\xdb\xc3\x05\x86\xaf3\xb1\x14\u04c9lk\x93[\x8b\xbd@\x00\x00\u07d4S*}\xa0\xa5\xadt\aF\x8d;\xe8\xe0~i\xc7\xddd\xe8a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4S-2\xb0\x0f0[\xcc$\xdc\xefV\x81}b/4\xfb,$\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4S4DX@\x82\xeb\xa6T\xe1\xad0\xe1Is\\o{\xa9\"\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4S8\xefp\xea\xc9\u075a\xf5\xa0P;^\xfa\xd1\x03\x9eg\xe7%\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94S9oJ&\u00b4`D\x960lTB\xe7\xfc\xba'.6\x8a\x04?/\b\xd4\x0eZ\xfc\x00\x00\xe0\x94S:s\xa4\xa2\"\x8e\xee\x05\xc4\xff\xd7\x18\xbb\xf3\xf9\xc1\xb1)\xa7\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4S<\x06\x92\x8f\x19\u0429V\xcc(\x86k\xf6\xc8\xd8\xf4\x19\x1a\x94\x89\x0f\xd8\xc1C8\xe60\x00\x00\u07d4S@e6\x1c\xb8T\xfa\xc4+\xfb\\\x9f\xcd\xe0`J\xc9\x19\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4SC\u007f\xec\xf3J\xb9\xd45\xf4\u07b8\xca\x18\x15\x19\xe2Y 5\x89\n1\x06+\xee\xedp\x00\x00\u07d4SR\x01\xa0\xa1\xd74\"\x80\x1fU\xde\xd4\u07ee\xe4\xfb\xaan;\x89\x02&!\x1fy\x15B\x80\x00\xe0\x94S`\x81\x05\xceK\x9e\x11\xf8k\xf4\x97\xff\xca;x\x96{_\x96\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4SnM\x80)\xb7?Uy\u0723>p\xb2N\xba\x89\xe1\x1d~\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Sp\rS%MC\x0f\"x\x1aJv\xa4c\x93;]k\b\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94S\u007f\x9dM1\xefp\x83\x9d\x84\xb0\xd9\u0377+\x9a\xfe\xdb\xdf5\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\xe0\x94S\x81D\x85\x03\xc0\xc7\x02T+\x1d\xe7\xcc_\xb5\xf6\xab\x1c\xf6\xa5\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94S\x94.yI\xd6x\x8b\xb7\x80\xa7\xe8\xa0y'\x81\xb1aK\x84\x8a\x03]\xebFhO\x10\xc8\x00\x00\u07d4S\x95\xa4E]\x95\xd1x\xb4S*\xa4r[\x19?\xfeQ)a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94S\x98\x9e\xd30V?\xd5}\xfe\u027d4<7`\xb0y\x93\x90\x8a\x01P\x89N\x84\x9b9\x00\x00\x00\u07d4S\xa2Dg(\x95H\x0fJ+\x1c\xdf}\xa5\xe5\xa2B\xecM\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4S\xa7\x14\xf9\x9f\xa0\x0f\xefu\x8e#\xa2\xe7F2m\xad$|\xa7\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4S\xaf2\xc2/\uf640?\x17\x8c\xf9\v\x80/\xb5q\xc6\x1c\xb9\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4S\xc0\xbb\u007f\u020e\xa4\"\xd2\xef~T\x0e-\x8f(\xb1\xbb\x81\x83\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94S\xc5\xfe\x01\x19\xe1\xe8Hd\f\xee0\xad\ua594\x0f*]\x8b\x8a\x04\x9a\xda_\xa8\xc1\f\x88\x00\x00\u07d4S\xc9\xec\xa4\ts\xf6;\xb5\x92{\xe0\xbcj\x8a\x8b\xe1\x95\x1ft\x89lk\x93[\x8b\xbd@\x00\x00\u07d4S\u0388\xe6lZ\xf2\U0009bf4fY*V\xa3\xd1_ l2\x89\a\xa2\x8c1\xcc6\x04\x00\x00\u07d4S\xce\xc6\u0200\x92\xf7V\xef\xe5o}\xb1\x12(\xa2\xdbE\xb1\"\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4S\xe3[\x12#\x1f\x19\xc3\xfdwL\x88\xfe\xc8\xcb\xee\xdf\x14\b\xb2\x89\x1b\xc1mgN\xc8\x00\x00\x00\u07d4S\xe4\xd9im\xcb?M{?p\u072aN\xec\xb7\x17\x82\xff\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4S\xfa\xf1e\xbe\x03\x1e\xc1\x830\xd9\xfc\xe5\xbd\x12\x81\xa1\xaf\b\u06c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4T\n\x18\x19\xbd|5\x86\x1ey\x18\x04\xe5\xfb\xb3\xbc\x97\u026b\xb1\x89N\xd7\xda\xc6B0 \x00\x00\xe0\x94T\f\a(\x02\x01N\xf0\xd5a4Z\xecH\x1e\x8e\x11\xcb5p\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94T\f\xf2=\xd9\\MU\x8a'\x9dw\x8d+75\xb3\x16A\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\x10`\xfcX\xc7P\xc4\x05\x12\xf83i\xc0\xa63@\xc1\"\xb6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\x13\xc9\u007f\xfaJn*{\xba\x89a\u071f\u03850\xa7\x87\u05c965\u026d\xc5\u07a0\x00\x00\u07d4T\x1d\xb2\n\x80\xcf;\x17\xf1b\x1f\x1b?\xf7\x9b\x88/P\xde\xf3\x8965\u026d\xc5\u07a0\x00\x00\u07d4T.\x80\x96\xba\xfb\x88\x16&\x06\x00.\x8c\x8a>\u0458\x14\xae\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\v:\xa8\x87\x03\xa7%\u07e5}\xe6\xe6F\x93Qd\x80,\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4T1\xb1\u0447Q\xb9\x8f\xc9\u220a\xc7u\x9f\x155\xa2\xdbG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T1\xcaB~ae\xa6D\xba\xe3&\xbd\tu\n\x17\x8ce\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T5\xc6\xc1y3\x17\xd3,\xe1;\xbaLO\xfe\xb9s\xb7\x8a\u0709\r\x8ek\x1c\x12\x85\xef\x00\x00\xe0\x94T6)\xc9\\\xde\xf4(\xad7\xd4S\u02958\xa9\xf9\t\x00\xac\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4T9\x1bM\x17mGl\xea\x16N_\xb55\u0197\x00\xcb%5\x89\x05l\xd5_\xc6M\xfe\x00\x00\xe0\x94T:\x8c\x0e\xfb\x8b\xcd\x15\xc5C\u29a4\xf8\aYv1\xad\xef\x8a\x01?\x80\xe7\xe1O-D\x00\x00\u07d4T?\x8cgN$b\xd8\xd5\u06a0\xe8\x01\x95\xa8p\x8e\x11\xa2\x9e\x89\x03wX\x83;:z\x00\x00\xe0\x94TK[5\x1d\x1b\xc8.\x92\x97C\x99H\xcfHa\xda\u026e\x11\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4TM\xdaB\x1d\xc1\xebs\xbb$\xe3\xe5j$\x80\x13\xb8|\x0fD\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4TW\\1\x14u\x1e<c\x19q\xdaj*\x02\xfd?\xfb\xfc\u0209i*\xe8\x89p\x81\xd0\x00\x00\u07d4T[\xb0p\xe7\x81\x17.\xb1`\x8a\xf7\xfc(\x95\xd6\u02c7\x19~\x89y\xa5\xc1~\xc7H\x90\x00\x00\xe0\x94Tu\xd7\xf1t\xbd\xb1\xf7\x89\x01||\x17\x05\x98\x96F\a\x9dI\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4T\x85X\u040c\xfc\xb1\x01\x18\x1d\xac\x1e\xb6\tKN\x1a\x89o\xa6\x89lj\xccg\u05f1\xd4\x00\x00\u07d4T\x93\x9f\xf0\x89!\xb4g\xcf)Fu\x1d\x85cx)lc\xed\x8965\u026d\xc5\u07a0\x00\x00\u07d4T\x9bGd\x9c\xfa\u0653\xe4\x06M&6\xa4\xba\xa0b3\x05\u0309 \x9d\x92/RY\xc5\x00\x00\u07d4T\x9dQ\xaf)\xf7$\xc9g\xf5\x94#\xb8[&\x81\xe7\xb1Q6\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4T\xa17\x01\x16\xfe\"\t\x9e\x01]\a\xcd&i\xdd)\x1c\xc9\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94T\xa6+\xf9#>\x14o\xfe\u00c7nE\xf2\x0e\xe8AJ\u07ba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4T\xb4B\x9b\x18/\x03w\xbe~bi9\xc5\xdbd@\xf7]z\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4T\xbc\xb8\xe7\xf7<\xda=s\xf4\u04cb-\bG\xe6\x00\xba\r\xf8\x89:pAX\x82\xdf\x18\x00\x00\u07d4T\xc9>\x03\xa9\xb2\xe8\xe4\xc3g(5\xa9\xeev\xf9a[\xc1N\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4T\u0388'YV\xde\xf5\xf9E\x8e;\x95\xde\xca\xcdH@!\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4T\xdb^\x06\xb4\x81]1\xcbV\xa8q\x9b\xa3:\xf2\xd7>rR\x89$R\x1e*0\x17\xb8\x00\x00\xe0\x94T\xe0\x12\x83\u030b8E8\xdddgp\xb3W\xc9`\xd6\xca\u034a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4T\xecs\x00\xb8\x1a\xc8C3\xed\x1b\x03<\xd5\u05e39r\xe24\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4T\xfe\xbc\xce \xfez\x90\x98\xa7U\xbd\x90\x98\x86\x02\xa4\x8c\b\x9e\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4U\n\xad\xae\x12!\xb0z\xfe\xa3\x9f\xba.\xd6.\x05\u5df5\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4U\f0o\x81\xef]\x95\x80\xc0l\xb1\xab \x1b\x95\xc7H\xa6\x91\x89$\x17\xd4\xc4p\xbf\x14\x00\x00\xe0\x94U\x19\x99\xdd\xd2\x05V3'\xb9\xb50xZ\xcf\xf9\xbcs\xa4\xba\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4U\x1ew\x84w\x8e\xf8\xe0H\xe4\x95\xdfI\xf2aO\x84\xa4\xf1\u0709 \x86\xac5\x10R`\x00\x00\xe0\x94U)\x83\na\xc1\xf1<\x19~U\v\xed\xdf\u05bd\x19\\\x9d\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U)\x87\xf0e\x1b\x91[.\x1eS(\xc1!\x96\rK\xddj\xf4\x89a\t=|,m8\x00\x00\u07d4U;k\x1cW\x05\x0e\x88\xcf\f1\x06{\x8dL\xd1\xff\x80\xcb\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U?7\xd9$fU\x0e\x9f\xd7u\xaet6-\xf00\x17\x912\x89lk\x93[\x8b\xbd@\x00\x00\u07d4UC6\xeeN\xa1U\xf9\xf2O\x87\xbc\xa9\xcar\xe2S\xe1,\u0489\x05k\xc7^-c\x10\x00\x00\u0794UC\xddm\x16\x9e\xec\x8a!;\xbfz\x8a\xf9\xff\xd1]O\xf7Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4UG\xfd\xb4\xae\x11\x95>\x01)+x\a\xfa\x92#\xd0\xe4`j\x89\x05]\x11}\xcb\x1d&\x00\x00\u07d4UR\xf4\xb3\xed>\x1d\xa7\x9a/x\xbb\x13\xe8\xaeZh\xa9\xdf;\x8965\u026d\xc5\u07a0\x00\x00\u07d4U\\\xa9\xf0\\\xc14\xabT\xae\x9b\xea\x1c?\xf8z\xa8Q\x98\u0289\x05k\xc7^-c\x10\x00\x00\xe0\x94U]\x8d<\xe1y\x8a\u0290'T\xf1d\xb8\xbe*\x022\x9cl\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4U]\xf1\x93\x90\xc1m\x01)\x87r\xba\xe8\xbc:\x11R\x19\x9c\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U^\xbe\x84\u06a4+\xa2V\xeax\x91\x05\xce\u0136\x93\xf1/\x18\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94U\u007f^e\xe0\xda3\x99\x82\x19\xadN\x99W\x05E\xb2\xa9\xd5\x11\x8a\x02U\x9c\xbb\x98XB@\x00\x00\u07d4U\x83` h\x83\xdd\x1bmJYc\x9eV)\xd0\xf0\xc6u\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4U\x84B0P\xe3\xc2\x05\x1f\v\xbd\x8fD\xbdm\xbc'\xec\xb6,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4U\x85)CI)p\xf8\xd6)\xa1Sf\xcd\xda\x06\xa9OE\x13\x89lk\x93[\x8b\xbd@\x00\x00\u0794U\x86d\x86\xec\x16\x8fy\xdb\xe0\u1af1\x88d\u0649\x91\xae,\x88\xdfn\xb0\xb2\xd3\xca\x00\x00\u07d4U\x8cTd\x9a\x8an\x94r+\xd6\xd2\x1d\x14qOqx\x054\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\x91\x940O\x14\xb1\xb9:\xfeDO\x06$\xe0S\xc2:\x00\t\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4U\x93\xc9\u0536ds\x0f\xd9<\xa6\x01Q\xc2\\.\xae\xd9<;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4U\x97\x06\xc32\xd2\ay\xc4_\x8am\x04ji\x91Y\xb7I!\x89\x14\x9bD.\x85\xa3\u03c0\x00\u07d4U\x98\xb3\xa7\x9aH\xf3+\x1f_\xc9\x15\xb8{d]\x80]\x1a\xfe\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4U\xa3\xdfW\xb7\xaa\xec\x16\xa1b\xfdS\x16\xf3[\xec\b(!\u03c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa4\xca\xc0\u02cbX-\x9f\xef8\xc5\xc9\xff\xf9\xbdS\t=\x1f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4U\xa6\x1b\x10\x94\x80\xb5\xb2\xc4\xfc\xfd\xef\x92\xd9\x05\x84\x16\f\r5\x89\x02lVM+S\xf6\x00\x00\u07d4U\xaa]1>\xbb\bM\xa0\xe7\x80\x10\x91\u2792\xc5\xde\u00ea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xab\x99\xb0\xe0\xe5]{\xb8t\xb7\xcf\xe84\xdec\x1c\x97\xec#\x897\xe9\x8c\xe3h\x99\xe4\x00\x00\u07d4U\xaf\t/\x94\xbajy\x91\x8b\f\xf99\xea\xb3\xf0\x1b?Q\u01c9\b \xd5\xe3\x95v\x12\x00\x00\u07d4U\xc5dfAf\xa1\xed\xf3\x91>\x01i\xf1\xcdE\x1f\xdb]\f\x89\x82\x17\xeaIP\x8el\x00\x00\xe0\x94U\xcaj\xbey\xea$\x97\xf4o\u06f804`\x10\xfeF\x9c\xbe\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4U\xca\xffK\xba\x04\xd2 \u0265\xd2\x01\x86r\xec\x85\xe3\x1e\xf8>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4U\xd0W\xbc\xc0K\xd0\xf4\xaf\x96BQ:\xa5\t\v\xb3\xff\x93\xfe\x89;\xfeE,\x8e\xddL\x00\x00\u07d4U\xd4.\xb4\x95\xbfF\xa64\x99{_.\xa3b\x81I\x18\u2c09\x05\xc0\xd2e\xb5\xb2\xa8\x00\x00\u07d4U\u069d\xcd\xcaa\xcb\xfe\x1f\x13<{\xce\xfc\x86{\x9c\x81\"\xf9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4U\xe2 \x87bb\xc2\x18\xafOVxG\x98\xc7\xe5]\xa0\x9e\x91\x89\a=\x99\xc1VE\xd3\x00\x00\u07d4U\xfd\b\u0440d\xbd ,\x0e\xc3\xd2\xcc\xe0\xce\v\x9d\x16\x9cM\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x00s\nU\xf6\xb2\x0e\xbd$\x81\x1f\xaa=\xe9m\x16b\xab\xab\x89e\xea=\xb7UF`\x00\x00\u07d4V\x03$\x1e\xb8\xf0\x8fr\x1e4\x8c\x9d\x9a\xd9/H\u342a$\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4V\x056yJ\x9e+\x00I\xd1\x023\xc4\x1a\xdc_A\x8a&J\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\aY\x00Y\xa9\xfe\xc1\x88\x11I\xa4K6\x94\x9a\xef\x85\xd5`\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V\v\xec\xdfR\xb7\x1f=\x88'\xd9'a\x0f\x1a\x98\x0f3qo\x89\x17GMp_V\u0400\x00\xe0\x94V\r\xa3~\x95m\x86/\x81\xa7_\u0540\xa7\x13\\\x1b$cR\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94V\x0f\xc0\x8d\a\x9f\x04~\xd8\xd7\xdfuU\x1a\xa55\x01\xf5p\x13\x8a\x01\x9b\xff/\xf5yh\xc0\x00\x00\u07d4V\x1b\xe9)\x9b>k>c\xb7\x9b\t\x16\x9d\x1a\x94\x8a\xe6\xdb\x01\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94V \xe3\xedy-/\x185\xfe_UA}Q\x11F\fj\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4V \xf4m\x14Q\xc25=bC\xa5\u0534'\x13\v\xe2\xd4\a\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94V!\x05\xe8+\t\x975\xdeI\xf6&\x92\u0307\xcd8\xa8\xed\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94V*\x8d\u02fe\xee\xf7\xb3`h]'0;\u059e\tJ\xcc\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4V+\xce\u04ca\xb2\xabl\b\x0f;\x05A\xb8Enp\x82K?\x89\"\xca5\x87\xcfN\xb0\x00\x00\xe0\x94V+\xe9Z\xba\x17\xc57\x1f\u2e82\x87\x99\xb1\xf5]!w\u058a\b\x16\xd3~\x87\xb9\xd1\xe0\x00\x00\u07d4V/\x16\u05da\xbf\xce\u00d4>4\xb2\x0f\x05\xf9{\xdf\u0366\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4V7=\xaa\xb4c\x16\xfd~\x15v\xc6\x1ej\xff\xcbeY\xdd\u05c9\v\xacq]\x14l\x9e\x00\x00\u07d4V9v8\xbb<\xeb\xf1\xf6 byK^\xb9B\xf9\x16\x17\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4V:\x03\xab\x9cV\xb6\x00\xf6\xd2[f\f!\xe1c5Qzu\x8965\u026d\xc5\u07a0\x00\x00\u07d4V<\xb8\x80<\x1d2\xa2['\xb6A\x14\x85+\xd0M\x9c \u0349\v\x14\x9e\xad\n\xd9\xd8\x00\x00\u07d4VXc\x91\x04\fW\xee\xc6\xf5\xaf\xfd\x8c\u052b\xde\x10\xb5\n\u0309\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Vl\x10\xd68\u8e0bG\xd6\xe6\xa4\x14Iz\xfd\xd0\x06\x00\u0509\x05k9Bc\xa4\f\x00\x00\u07d4Vl(\xe3L8\b\xd9vo\xe8B\x1e\xbfO+\x1cO}w\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4V\x8d\xf3\x18Vi\x9b\xb5\xac\xfc\x1f\xe1\u0580\u07d9`\xcaCY\x89J\xcfUR\xf3\xb2I\x80\x00\u07d4V\x91\xdd/gE\xf2\x0e\"\xd2\xe1\u0479U\xaa)\x03\xd6VV\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4V\xa1\xd6\r@\xf5\u007f0\x8e\xeb\xf0\x87\xde\xe3\xb3\u007f\x1e|,\xba\x89>\u072e\xc8-\x06\xf8\x00\x00\u07d4V\xac \xd6;\xd8\x03Y\\\xec\x03m\xa7\xed\x1d\xc6n\n\x9e\a\x89\x03w*S\xcc\xdce\x80\x00\u07d4V\xb6\xc2=\xd2\uc434r\x8f;\xb2\xe7d\xc3\xc5\f\x85\xf1D\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xdf\x05\xba\xd4l?\x00\xaeGn\xcf\x01{\xb8\xc8w8?\xf1\x89\n\xb1]\xaa\xefp@\x00\x00\u07d4V\xee\x19\u007fK\xbf\x9f\x1b\x06b\xe4\x1c+\xbd\x9a\xa1\xf7\x99\xe8F\x8965\u026d\xc5\u07a0\x00\x00\u07d4V\xf4\x93\xa3\xd1\b\xaa\xa2\u044d\x98\x92/\x8e\xfe\x16b\u03f7=\x89m\x81!\xa1\x94\xd1\x10\x00\x00\u07d4V\xfc\x1a{\xad@G#|\xe1\x16\x14b\x96#\x8e\a\x8f\x93\xad\x89\t\xa6?\b\xeac\x88\x00\x00\u07d4V\xfe\xbf\x9e\x10\x03\xaf\x15\xb1\xbdI\a\xec\b\x9aJ\x1b\x91\xd2h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\x17\u0313\x01Q\x1dJ\x81\xb9\xf5\x83\x14\x8b\xee\xd3\xd3\u0303\t\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4W\x17\xf2\xd8\xf1\x8f\xfc\xc0\xe5\xfe$}:B\x19\x03|:d\x9c\x89\u063beI\xb0+\xb8\x00\x00\u07d4W\x19P\xea,\x90\xc1B}\x93\x9da\xb4\xf2\xdeL\xf1\u03ff\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4W\x19\xf4\x9br\r\xa6\x88V\xf4\xb9\xe7\b\xf2VE\xbd\xbcKA\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4W*\xc1\xab\xa0\xde#\xaeA\xa7\xca\xe1\xdc\bB\u062b\xfc\x10;\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94W-\xd8\xcd?\xe3\x99\xd1\xd0\xec(\x121\xb7\xce\xfc \xb9\u4eca\x023\xc8\xfeBp>\x80\x00\x00\xe0\x94WI!\x83\x8c\xc7}l\x98\xb1}\x90::\xe0\xee\r\xa9[\u040a\vS(\x17\x8a\xd0\xf2\xa0\x00\x00\u07d4WJ\xd95S\x90\u421e\xf4*\xcd\x13\x8b*'\xe7\x8c\x00\xae\x89Tg\xb72\xa9\x134\x00\x00\u07d4WM\xe1\xb3\xf3\x8d\x91XF\xae7\x18VJZ\xda \xc2\xf3\xed\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94W\\\x00\u0081\x82\x10\u0085U\xa0\xff)\x01\x02\x89\xd3\xf8#\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94Ws\xb6\x02g!\xa1\xdd\x04\xb7\x82\x8c\xd6+Y\x1b\xfb4SL\x8a\x05\xb7\xacES\xdez\xe0\x00\x00\xe0\x94WwD\x1c\x83\xe0?\v\xe8\xdd4\v\xdechP\x84|b\v\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Wx\xff\u071b\x94\u0165\x9e\"N\xb9e\xb6\u0790\xf2\"\xd1p\x89\x12-\u007f\xf3f\x03\xfc\x00\x00\u07d4Wz\xee\xe8\u053c\b\xfc\x97\xab\x15n\xd5\u007f\xb9p\x92Sf\xbe\x89\x12\r\xf1\x14rX\xbf\x00\x00\u07d4W{-\a<Y\fP0o[\x11\x95\xa4\xb2\xba\x9e\u0366%\x89\x14@\xbd\u0515\x15\xf0\x00\x00\u07d4W{\xfed\xe3\xa1\xe3\x80\x0e\x94\xdb\x1cl\x18M\x8d\u022a\xfcf\x89Q4\xed\x17A\u007f(\x00\x00\u07d4W\x82Z\xeb\t\al\xaaGx\x87\xfb\u026e7\xe8\xb2|\xc9b\x89\x05k\xc7^-c\x10\x00\x00\u07d4W\x880\x10\xb4\xac\x85\u007f\xed\xac\x03\xea\xb2U\x17#\xa8D\u007f\xfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4W\x89\xd0\x1d\xb1,\x81j\xc2h\xe9\xaf\x19\xdc\r\xd6\u065f\x15\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\x92\x81OY\xa3:\x18C\xfa\xa0\x1b\xaa\b\x9e\xb0/\xfb\\\xf1\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4W\x93\xab\xe6\xf1S3\x11\xfdQSh\x91x;?\x96%\xef\x1c\x89,\u0626V\xf2?\xda\x00\x00\xe0\x94W\x97\xb6\x0f\u0489J\xb3\xc2\xf4\xae\u0786\xda\xf2\xe7\x88\xd7E\xad\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4W\xa8R\xfd\xb9\xb1@[\xf5<\u03d5\b\xf82\x99\xd3 lR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4W\xb2=j\x1a\xdc\x06\xc6R\xa7y\u01a7\xfbk\x95\xb9\xfe\xadf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\xbc \xe2\xd6+=\x19f<\xdbL0\x9d[O/\xc2\u06cf\x89\x05k\xc7^-c\x10\x00\x00\u07d4W\xbd\xdf\a\x884\x00\x9c\x89\u060eb\x82u\x9d\xc4S5\xb4p\x89tq|\xfbh\x83\x10\x00\x00\u07d4W\xbe\xeaql\xbd\x81p\ns\xd6\u007f\x9f\xf09R\x9c-\x90%\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4W\xd02\xa4=\x16Nq\xaa.\xf3\xff\xd8I\x1b\nN\xf1\xea[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4W\xd3\u07c0O+\xee\xe6\xefS\xab\x94\xcb>\xe9\xcfRJ\x18\u04c9\x15Vak\x96\x06g\x00\x00\u07d4W\xd5\xfd\x0e=0I3\x0f\xfc\xdc\xd0 Ei\x17e{\xa2\u0689k\xf2\x01\x95\xf5T\xd4\x00\x00\u07d4W\u0754q\xcb\xfa&'\t\xf5\U00106f37t\xc5\xf5'\xb8\xf8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4W\xdf#\xbe\xbd\xc6^\xb7_\ub732\xfa\xd1\xc0si++\xaf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4X\x00\u03410\x83\x9e\x94I]-\x84\x15\xa8\xea,\x90\xe0\xc5\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94X\x03\xe6\x8b4\xda\x12\x1a\xef\b\xb6\x02\xba\u06ef\xb4\xd1$\x81\u028a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\xe0\x94X\x16\xc2hww\xb6\xd7\u04a2C-Y\xa4\x1f\xa0Y\xe3\xa4\x06\x8a\x1cO\xe4:\xdb\n^\x90\x00\x00\u07d4X\x1a:\xf2\x97\xef\xa4Cj)\xaf\x00r\x92\x9a\xbf\x98&\xf5\x8b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\x1b\x9f\xd6\xea\xe3r\xf3P\x1fB\xeb\x96\x19\xee\xc8 \xb7\x8a\x84\x8a\x04+\xe2\xc0\f\xa5;\x8d\x80\x00\u07d4X\x1b\xdf\x1b\xb2v\xdb\u0746\xae\xdc\xdb9z\x01\xef\xc0\xe0\f[\x8965\u026d\xc5\u07a0\x00\x00\u07d4X\x1f4\xb5#\xe5\xb4\x1c\t\xc8|)\x8e)\x9c\xbc\x0e)\xd0f\x89=X3\xaa\xfd9u\x80\x00\xe0\x94X$\xa7\xe2(8'q40\x8c_KP\u06b6^C\xbb1\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4X+pf\x9c\x97\xaa\xb7\u0581H\xd8\xd4\xe9\x04\x11\xe2\x81\rV\x8965f3\xeb\xd8\xea\x00\x00\u07d4X.|\xc4o\x1d{Nn\x9d\x95\x86\x8b\xfd7\x05s\x17\x8fL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X>\x83\xbaU\xe6~\x13\xe0\xe7o\x83\x92\xd8s\xcd!\xfb\xf7\x98\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Xi\xfb\x86}q\xf18\u007f\x86;i\x8d\t\xfd\xfb\x87\u011b\\\x89\u01bb\xf8X\xb3\x16\b\x00\x00\u07d4X}hI\xb1h\xf6\xc33+z\xba\xe7\xeblB\xc3\u007fH\xbf\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4X\x87\xdcj3\xdf\xedZ\xc1\xed\xef\xe3^\xf9\x1a!b1\xac\x96\x89\r\x8drkqw\xa8\x00\x00\xe0\x94X\x8e\u0650\xa2\xaf\xf4J\x94\x10]X\xc3\x05%w5\xc8h\xac\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4X\xae-\xdc_L\x8a\u0697\xe0l\x00\x86\x17\x17g\xc4#\xf5\u05c9WG=\x05\u06ba\xe8\x00\x00\u07d4X\xae\xd6gJ\xff\xd9\xf6B3'*W\x8d\xd98k\x99\xc2c\x89\xb8Pz\x82\a( \x00\x00\xe0\x94X\xb8\b\xa6[Q\xe63\x89i\xaf\xb9^\xc7\a5\xe4Q\xd5&\x8a\bxK\xc1\xb9\x83z8\x00\x00\u07d4X\xb8\xae\x8fc\xef5\xed\ab\xf0\xb6#=J\xc1Nd\xb6M\x89lk\x93[\x8b\xbd@\x00\x00\u07d4X\xba\x15ie\x0e[\xbb\xb2\x1d5\xd3\xe1u\xc0\u05b0\xc6Q\xa9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4X\xc5U\xbc)<\xdb\x16\xc66.\xd9z\xe9U\v\x92\xea\x18\x0e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4X\xc6P\xce\xd4\v\xb6VA\xb8\xe8\xa9$\xa09\xde\xf4hT\u07c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4X\xc9\aT\xd2\xf2\n\x1c\xb1\xdd3\x06%\xe0KE\xfaa\x9d\\\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xe2\xf1\x12#\xfc\x827\xf6\x9d\x99\xc6(\x9c\x14\x8c\x06\x04\xf7B\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4X\xe5T\xaf=\x87b\x96 \xdaa\xd58\xc7\xf5\xb4\xb5LJ\xfe\x89FP\x9diE4r\x80\x00\u07d4X\xe5\xc9\xe3D\xc8\x06e\r\xac\xfc\x90M3\xed\xbaQ\a\xb0\u0789\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4X\xe6a\u043as\xd6\xcf$\t\x9aUb\xb8\b\xf7\xb3g;h\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94X\xf0[&%`P<\xa7a\xc6\x18\x90\xa4\x03_Lsr\x80\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4X\xfb\x94sd\xe7iWe6\x1e\xbb\x1e\x80\x1f\xfb\x8b\x95\xe6\u0409\n\u05ce\xbcZ\xc6 \x00\x00\u07d4Y\x01\x81\xd4E\x00{\u0407Z\xaf\x06\x1c\x8dQ\x159\x00\x83j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x02\xe4J\xf7i\xa8rF\xa2\x1e\a\x9c\b\xbf6\xb0n\xfe\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\n\xcb\xda7)\f\r>\xc8O\xc2\x00\rv\x97\xf9\xa4\xb1]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94Y\f\xcbY\x11\xcfx\xf6\xf6\"\xf55\xc4t7_J\x12\xcf\u03ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y\x10\x10m\xeb\u0491\xa1\u0340\xb0\xfb\xbb\x8d\x8d\x9e\x93\xa7\xcc\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\x16\x17I\xfe\xdc\xf1\xc7!\xf2 -\x13\xad\xe2\xab\xcfF\v=\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Y\x1b\xef1q\xd1\u0155w\x17\xa4\xe9\x8d\x17\xeb\x14,!NV\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y <\xc3u\x99\xb6H1*|\xc9\xe0m\xac\xb5\x89\xa9\xaej\x89\b\x0fyq\xb6@\x0e\x80\x00\u07d4Y&\x81q\xb83\xe0\xaa\x13\xc5KR\xcc\xc0B.O\xa0:\ub262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94Y'w&\x1e;\xd8R\u010e\u0295\xb3\xa4L[\u007f-B,\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4Y0Dg\x0f\xae\xff\x00\xa5[Z\xe0Q\xeb{\xe8p\xb1\x16\x94\x89\a?u\u0460\x85\xba\x00\x00\xe0\x94Y;E\xa1\x86J\xc5\xc7\xe8\xf0\u02ae\xba\r\x87<\xd5\xd1\x13\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Y<H\x93[\xea\xff\x0f\xde\x19\xb0M0\x9c\xd50\xa2\x8eR\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4YG<\xd3\x00\xff\xfa\xe2@\xf5xV&\xc6]\xfe\u01d2\xb9\xaf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4YH\xbc6P\xedQ\x9b\xf8\x91\xa5rg\x9f\u0652\xf8x\fW\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4YJv\xf0i58\x8d\xde^#F\x96\xa0f\x8b\xc2\r-\u0709\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4YV\x9a!\u048f\xbaK\xda7u4\x05\xa0\x81\xf2\x06=\xa1P\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4YV\xb2\x8e\u01c9\vv\xfc\x06\x1a\x1f\xebR\xd8*\xe8\x1f\xb65\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y^#\u05c8\xa2\u053b\x85\xa1]\xf7\x13m&JcU\x11\xb3\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94Yp8\xff\x91\xa0\x90\f\xbb\xabH\x8a\xf4\x83\u01d0\xe6\xec\x00\xa0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94Yp\xfb\x1b\x14M\xd7Q\xe4\xce.\xca|\xaa \xe3c\xdcM\xa3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Yu\xb9R\x8f#\xaf\x1f\x0e.\xc0\x8a\xc8\xeb\xaaxj,\xb8\xe0\x89\x12\xbfPP:\xe3\x03\x80\x00\u07d4Yu\u05cd\x97N\u5edeML\xa2\xaew\xc8K\x9c;K\x82\x89JD\x91\xbdm\xcd(\x00\x00\u07d4Y\x85\u015aD\x9d\xfc]\xa7\x87\xd8$Ntlmp\u02a5_\x89\x05k\xc7^-c\x10\x00\x00\u07d4Y\x8a\xaa\xba\xe9\xed\x83={\xc2\"\xe9\x1f\u02a0d{wX\v\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4Y\x92bLT\xcd\xec`\xa5\xae\x93\x803\xaf\x8b\xe0\xc5\f\xbb\n\x89\xc4T\xe0\xf8\x87\x0f+\x00\x00\xe0\x94Y\x97(\xa7\x86\x18\u0461{\x9e4\xe0\xfe\xd8\xe8W\xd5\xc4\x06\"\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4Y\x97\xff\xef\xb3\xc1\xd9\xd1\x0f\x1a\u2b0a\xc3\xc8\xe2\xd2)'\x83\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\xa0\x87\xb95\x1c\xa4/X\xf3n\x02\x19'\xa2)\x88(O8\x89\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4Y\xa1-\xf2\xe3\xef\x85z\xce\xff\x93\x06\xb3\t\xf6\xa5\x00\xf7\x014\x8965\u026d\xc5\u07a0\x00\x00\u07d4Y\xb9m\ub1c4\x88]\x8d;J\x16aC\xccC]%U\xa1\x89Hz\x9a0E9D\x00\x00\u07d4Y\xb9\xe73\u02e4\xbe\x00B\x9bK\xd9\u07e6G2\x05:}U\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Y\xc5\xd0k\x17\x0e\xe4\xd2n\xb0\xa0\xebF\xcb}\x90\xc1\xc9\x10\x19\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4Y\xc7\xf7\x85\xc91`\xe5\x80~\xd3N^SK\xc6\x18\x86G\xa7\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4Y\xd19\xe2\xe4\f{\x97#\x9d#\u07ec\xa38X\xf6\x02\xd2+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Y\xf6${\rX*\xaa%\xe5\x11Ge\xe4\xbf<wOC\u0089\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4Y\xfe\x00im\xbd\x87\xb7\x97k)\xd1\x15l\x88B\xa2\xe1y\x14\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94Z\r`\x9a\xae#2\xb17\xab;/&aZ\x80\x8f7\xe43\x8a!\xe1\x9e\f\x9b\xab$\x00\x00\x00\u07d4Z\x19+\x96J\xfd\x80w>_^\xdajV\xf1N%\xe0\xc6\xf3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4Z\x1a3ib\xd6\xe0\xc601\u0303\u01a5\u01a6\xf4G\x8e\u02c965\u026d\xc5\u07a0\x00\x00\u07d4Z\x1d--\x1dR\x03\x04\xb6 \x88IW\x047\xeb0\x91\xbb\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4Z&s1\xfa\xcb&-\xaa\xec\xd9\xddc\xa9p\f_RY\u07c9\x05k\xc7^-c\x10\x00\x00\xe0\x94Z(WU9\x1e\x91NX\x02_\xaaH\xcch_O\xd4\xf5\xb8\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4Z)\x16\xb8\xd2\xe8\xcc\x12\xe2\a\xabFMC>#p\xd8#\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4Z+\x1c\x85:\xeb(\xc4U9\xafv\xa0\n\xc2\u0628$(\x96\x89\x01Z\xf1\u05cbX\xc4\x00\x00\u07d4Z-\xaa\xb2\\1\xa6\x1a\x92\xa4\xc8,\x99%\xa1\xd2\xefXX^\x89\f8\r\xa9\u01d5\f\x00\x00\u07d4Z0\xfe\xac7\xac\x9fr\u05f4\xaf\x0f+\xc79R\xc7O\xd5\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4ZTh\xfa\\\xa2&\xc7S.\xcf\x06\xe1\xbc\x1cE\"]~\u0249g\x8a\x93 b\xe4\x18\x00\x00\u07d4ZVR\x857JI\xee\xddPL\x95}Q\bt\xd0\x04U\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4Z^\xe8\xe9\xbb\x0e\x8a\xb2\xfe\xcbK3\u0494x\xbeP\xbb\xd4K\x89*\x11)\u0413g \x00\x00\xe0\x94Z_\x85\b\xda\x0e\xbe\xbb\x90\xbe\x903\xbdM\x9e'A\x05\xae\x00\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4Z`q\xbc\xeb\xfc\xbaJ\xb5\u007fM\xb9o\u01e6\x8b\xec\xe2\xba[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z`\xc9$\x16(s\xfc~\xa4\xda\u007f\x97.5\x01g7`1\x89\x04\x87\xf2w\xa8\x85y\x80\x00\u07d4Zf\x86\xb0\xf1~\a\xed\xfcY\xb7Y\xc7}[\xef\x16M8y\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4Zp\x10o \xd6?\x87Re\xe4\x8e\r5\xf0\x0e\x17\xd0+\u0249\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794Zt\xbab\xe7\xc8\x1a4t\xe2}\x89O\xed3\xdd$\xad\x95\xfe\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94Zw5\x00}p\xb0hD\u0699\x01\xcd\xfa\xdb\x11\xa2X,/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4Z\x82\xf9l\u0537\xe2\xd9=\x10\xf3\x18]\xc8\xf4=Ku\xaai\x89lc?\xba\xb9\x8c\x04\x00\x00\u07d4Z\x87\xf04\xe6\xf6\x8fNt\xff\xe6\fd\x81\x946\x03l\xf7\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\x89\x11U\xf5\x0eB\aCt\xc79\xba\xad\xf7\xdf&Q\x15:\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\u07d4Z\x9c\x8bi\xfcaMiVI\x99\xb0\r\xcbB\xdbg\xf9~\x90\x89\xb9\xe6\x15\xab\xad:w\x80\x00\xe0\x94Z\xaf\x1c1%Jn\x00_\xba\u007fZ\xb0\xecy\xd7\xfc+c\x0e\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4Z\xb1\xa5aSH\x00\x1c|w]\xc7WHf\x9b\x8b\xe4\xde\x14\x89%jr\xfb)\xe6\x9c\x00\x00\xe1\x94Z\xbf\xec%\xf7L\u06047c\x1aw1\x90i2wcV\xf9\x8b\t\xd8<\xc0\u07e1\x11w\xff\x80\x00\u07d4Z\u0090\x8b\x0f9\x8c\r\xf5\xba\xc2\xcb\x13\xcas\x14\xfb\xa8\xfa=\x89\n\xd4\xc81j\v\f\x00\x00\xe0\x94Z\u025a\u05c1j\xe9\x02\x0f\xf8\xad\xf7\x9f\xa9\x86\x9b|\xeaf\x01\x8a\x04ri\x8bA;C \x00\x00\u07d4Z\xd1,^\xd4\xfa\x82~!P\u03e0\u058c\n\xa3{\x17i\xb8\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94Z\xd5\xe4 uV\x13\x88o5\xaaV\xac@>\xeb\xdf\xe4\xb0\u040a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4Z\xdew\xfd\x81\xc2\\\n\xf7\x13\xb1\a\x02v\x8c\x1e\xb2\xf9u\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4Z\xe6N\x85;\xa0\xa5\x12\x82\u02cd\xb5.Aa^|\x9fs?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xed\x0el\xfe\x95\xf9\u0580\xc7dr\xa8\x1a+h\n\u007f\x93\xe2\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4Z\xef\x16\xa2&\xddh\a\x1f$\x83\xe1\xdaBY\x83\x19\xf6\x9b,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4Z\xf4j%\xac\t\xcbsakS\xb1O\xb4/\xf0\xa5\x1c\u0772\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4Z\xf7\xc0r\xb2\u016c\xd7\x1cv\xad\xdc\xceS\\\xf7\xf8\xf95\x85\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94Z\xfd\xa9@\\\x8e\x976QEt\u0692\x8d\xe6tV\x01\t\x18\x8a\x01E\xb8\xb0#\x9aF\x92\x00\x00\u07d4[\x06\xd1\xe6\x93\f\x10Ti+y\xe3\xdb\xe6\xec\xceS\x96d \x89\v\"\u007fc\xbe\x81<\x00\x00\u07d4[%\xca\xe8m\xca\xfa*`\xe7r61\xfc_\xa4\x9c\x1a\xd8}\x89\x87\fXQ\x0e\x85 \x00\x00\u07d4[(|~sB\x99\xe7'bo\x93\xfb\x11\x87\xa6\rPW\xfe\x89\x05|\xd94\xa9\x14\xcb\x00\x00\u07d4[)\f\x01\x96|\x81.M\xc4\xc9\v\x17L\x1b@\x15\xba\xe7\x1e\x89\b \xeb4\x8dR\xb9\x00\x00\u07d4[+d\xe9\xc0X\u30a8\xb2\x99\"N\xec\xaa\x16\xe0\x9c\x8d\x92\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\xe0\x94[./\x16\x18U.\xab\r\xb9\x8a\xddUc|)Q\xf1\xfb\x19\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4[0`\x8cg\x8e\x1a\xc4d\xa8\x99L;3\xe5\xcd\xf3Iq\x12\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[36\x96\xe0L\xca\x16\x92\xe7\x19\x86W\x9c\x92\rk)\x16\xf9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94[C\rw\x96\x96\xa3e?\xc6\x0et\xfb\u02ec\xf6\xb9\u00ba\xf1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4[Cse\xae:\x9a/\xf9|h\xe6\xf9\nv \x18\x8c}\x19\x89l\x87T\xc8\xf3\f\b\x00\x00\u07d4[I\xaf\xcduDx8\xf6\xe7\xce\u068d!w}O\xc1\xc3\xc0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4[L\f`\xf1\x0e\u0489K\xdbB\xd9\xdd\x1d!\x05\x87\x81\n\r\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4[N\xa1m\xb6\x80\x9b\x03R\u0536\xe8\x1c9\x13\xf7jQ\xbb2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4[[\xe0\xd8\xc6rv\xba\xab\xd8\xed\xb3\rH\xeaud\v\x8b)\x89,\xb1\xf5_\xb7\xbe\x10\x00\x00\u07d4[]Qp)2\x15b\x11\x1bC\bm\v\x045\x91\x10\x9ap\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94[]\x8c\x8e\xedl\x85\xac!Va\xde\x02fv\x82?\xaa\n\f\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4[mU\xf6q)g@\\e\x91)\xf4\xb1\xde\t\xac\xf2\xcb{\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4[p\u011c\u024b=\xf3\xfb\xe2\xb1Y\u007f\\\x1bcG\xa3\x88\xb7\x894\x95tD\xb8@\xe8\x00\x00\u07d4[sn\xb1\x83Sb\x9b\u0796v\xda\xdd\x16P4\xce^\xcch\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[u\x9f\xa1\x10\xa3\x1c\x88F\x9fT\xd4K\xa3\x03\xd5}\xd3\xe1\x0f\x89[F\xdd/\x0e\xa3\xb8\x00\x00\u07d4[w\x84\xca\xea\x01y\x9c\xa3\x02'\x82vg\xce |\\\xbcv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4[x\xec\xa2\u007f\xbd\xeao&\xbe\xfb\xa8\x97+)^x\x146K\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94[\x80\v\xfd\x1b>\u0525}\x87Z\xed&\xd4/\x1aw\b\xd7*\x8a\x01Z\x82\xd1\u057b\x88\xe0\x00\x00\u07d4[\x85\xe6\x0e*\xf0TO/\x01\xc6N 2\x90\x0e\xbd8\xa3\u01c9lk\x93[\x8b\xbd@\x00\x00\u07d4[\xa2\xc6\xc3]\xfa\xec)h&Y\x19\x04\xd5DFJ\xea\xbd^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94[\xafmt\x96 \x80>\x83H\xaf7\x10\xe5\xc4\xfb\xf2\x0f\u0214\x8a\x01\x0f@\x02a]\xfe\x90\x00\x00\u07d4[\xc1\xf9U\a\xb1\x01\x86B\xe4\\\xd9\xc0\xe2'3\xb9\xb1\xa3&\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94[\xd25GG\u007fm\t\u05f2\xa0\x05\xc5\xeee\fQ\fV\u05ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4[\xd2J\xac6\x12\xb2\f`\x9e\xb4gy\xbf\x95i\x84\a\xc5|\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4[\u0586-Q}M\xe4U\x9dN\xec\n\x06\xca\xd0^/\x94n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4[\xe0EQ*\x02n?\x1c\xeb\xfdZ~\xc0\xcf\xc3o-\xc1k\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94[\xf9\xf2\"nZ\xea\xcf\x1d\x80\xae\nY\xc6\xe3\x808\xbc\x8d\xb5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4[\xfa\xfe\x97\xb1\xdd\x1dq+\xe8mA\xdfy\x89SE\x87Z\x87\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\\\x0f.Q7\x8fk\r{\xabas1X\vn9\xad<\xa5\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\\)\xf9\xe9\xa5#\xc1\xf8f\x94H\xb5\\H\xcb\xd4|%\xe6\x10\x894F\xa0\xda\xd0L\xb0\x00\x00\xe0\x94\\0\x8b\xacHW\xd3;\xae\xa0t\xf3\x95m6!\xd9\xfa(\xe1\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\\1*V\u01c4\xb1\"\t\x9bvM\x05\x9c!\xec\xe9^\x84\u0289\x05&c\u032b\x1e\x1c\x00\x00\u07d4\\1\x99m\xca\xc0\x15\xf9\xbe\x98[a\x1fF\x870\xef$M\x90\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\\24W\xe1\x87v\x1a\x82v\xe3Y\xb7\xb7\xaf?;n=\xf6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\<\x1cd[\x91uC\x11;>l\x1c\x05M\xa1\xfet+\x9a\x89+^:\xf1k\x18\x80\x00\x00\u0794\\=\x19D\x1d\x19l\xb4Cf \xfc\xad\u007f\xbby\xb2\x9ex\x88\xc6s\xce<@\x16\x00\x00\u07d4\\?V\u007f\xaf\xf7\xba\u0475\x12\x00\"\xe8\xcb\u02a8+I\x17\xb3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\\Ch\x91\x8a\xced\t\u01de\u0280\u036a\xe49\x1d+bN\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\\FA\x97y\x1c\x8a=\xa3\xc9%Co'z\xb1;\xf2\xfa\xa2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\\H\x81\x16\\\xb4+\xb8.\x979l\x8e\xf4J\xdb\xf1s\xfb\x99\x89\x05\xfe\xe2\"\x04\x1e4\x00\x00\xe0\x94\\H\x92\x90z\a \xdfo\xd3A>c\xffv}k9\x80#\x8a\x02\xcb\x00\x9f\u04f5y\x0f\x80\x00\u07d4\\O$\xe9\x94\ud3c5\x0e\xa7\x81\x8fG\x1c\x8f\xac;\xcf\x04R\x89]\x80h\x8d\x9e1\xc0\x00\x00\u07d4\\T\x19V\\:\xadNqN\a92\x8e5!\u024f\x05\u0309\x1c\x9fx\u0489>@\x00\x00\u07d4\\a6\xe2\x18\xde\na\xa17\xb2\xb3\x96-*a\x12\xb8\t\u05c9\x0f\xf3\u06f6_\xf4\x86\x80\x00\xe0\x94\\a\xaby\xb4\b\xdd2)\xf6bY7\x05\xd7/\x1e\x14{\xb8\x8a\x04\xd0$=4\x98\u0344\x00\x00\u07d4\\m\x04\x1d\xa7\xafD\x87\xb9\xdcH\xe8\xe1\xf6\af\u0425m\xbc\x89O\a\n\x00>\x9ct\x00\x00\u07d4\\o6\xaf\x90\xab\x1aeln\xc8\xc7\xd5!Q'b\xbb\xa3\xe1\x89lh\xcc\u041b\x02,\x00\x00\u07d4\\{\x9e\u01e2C\x8d\x1e<v\x98\xb5E\xb9\xc3\xfdw\xb7\xcdU\x8965\u026d\xc5\u07a0\x00\x00\u07d4\\\x93o;\x9d\"\xc4\x03\xdb^s\x0f\xf1w\xd7N\xefB\u06ff\x89\x04\x10\u0546\xa2\nL\x00\x00\u07d4\\\xb71\x16\r.\x89eg\v\u0792]\x9d\xe5Q\t54}\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\\\xb9S\xa0\xe4/P0\x81\"&!\u007f\xff\xc3\xce#\x04W\xe4\x89\x05k\xc7^-c\x10\x00\x00\u0794\\\xbd\x8d\xaf'\xdd\xf7\x04\xcd\xd0\xd9\t\xa7\x89\xba6\xedO7\xb2\x88\xb9\xf6]\x00\xf6<\x00\x00\xe0\x94\\\xc4\u02e6!\xf2 cwB\x05\u007f`U\xb8\r\xff\xd7~\x13\x8a\bxG{}%;f\x00\x00\xe0\x94\\\xc7\xd3\x06mE\xd2v!\xf7\x8b\xb4\xb39G>D*\x86\x0f\x8a\x02\x1e\x18\x99\xf07z\xea\x00\x00\u07d4\\\xcc\xf1P\x8b\xfd5\xc2\x050\xaad%\x00\xc1\r\xeee\xea\xed\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\\\xcer\xd0h\xc7\xc3\xf5[\x1d(\x19T^w1|\xae\x82@\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\\\xd0\xe4u\xb5D!\xbd\xfc\f\x12\xea\x8e\b+\u05e5\xaf\nj\x89\x032\xca\x1bg\x94\f\x00\x00\u07d4\\\u0548\xa1N\xc6H\xcc\xf6G)\xf9\x16z\xa7\xbf\x8b\xe6\xeb=\x8965\u026d\xc5\u07a0\x00\x00\u07d4\\\u062f`\xdee\xf2M\xc3\xceW0\xba\x92e0\"\xdcYc\x89a\t=|,m8\x00\x00\u07d4\\\xdcG\b\xf1O@\xdc\xc1Zy_}\xc8\xcb\v\u007f\xaa\x9en\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\\\u0d86,\u0391b\xe8~\bI\xe3\x87\xcb]\xf4\xf9\x11\x8c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\\\xe2\xe7\u03aa\xa1\x8a\xf0\xf8\xaa\xfa\u007f\xba\xd7L\u021e<\xd46\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\\\xe4@h\xb8\xf4\xa3\xfey\x9ej\x83\x11\xdb\xfd\xed\xa2\x9d\xee\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u0794\\\xeb\xe3\v*\x95\xf4\xae\xfd\xa6ee\x1d\xc0\xcf~\xf5u\x81\x99\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\\\xf1\x8f\xa7\u0227\xc0\xa2\xb3\xd5\xef\u0459\x0fd\xdd\xc5i$,\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\\\xf4N\x10T\reqd#\xb1\xbc\xb5B\xd2\x1f\xf8:\x94\u034a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\\\xf8\xc0>\xb3\xe8r\xe5\x0f|\xfd\f/\x8d;?,\xb5\x18:\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\\\xfa\x8dV\x85ue\x8c\xa4\xc1\xa5\x93\xacL]\x0eD\xc6\aE\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\\\xfa\x98w\xf7\x19\u01dd\x9eIJ\b\xd1\xe4\x1c\xf1\x03\xfc\x87\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]\x1d\xc38{G\xb8E\x1eU\x10l\f\xc6}m\xc7+\u007f\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]#\x1ap\xc1\xdf\xeb6\n\xbd\x97\xf6\x16\xe2\xd1\r9\xf3\u02b5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4]$\xbd\xbc\x1cG\xf0\xeb\x83\xd1(\xca\xe4\x8a\xc3<H\x17\xe9\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4](\x19\xe8\xd5x!\x92.\xe4Ee\f\xca\xec}@TJ\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]/\u007f\v\x04\xbaK\xe1a\u1736\xf1\x12\xcez^}\u007f\xe4\x89\x01\xe8\u007f\x85\x80\x9d\xc0\x00\x00\u07d4]2\xf6\xf8nx\u007f\xf7\x8ec\u05cb\x0e\xf9_\xe6\a\x18R\xb8\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4]9\uf7a6\xbd\xff\xf1]\x11\xfe\x91\xf5a\xa6\xf9\xe3\x1f]\xa5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94]?;\x1fq0\xb0\xbb!\xa0\xfd29b9\x17\x9a%e\u007f\x8a\r:\xb8\xea^\x8f\xd9\xe8\x00\x00\u07d4]WQ\x81\x9bO=&\xed\f\x1a\xc5qU'5'\x1d\xbe\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4]\\,\x10\x99\xbb\xee\xfb&~t\xb5\x88\x80\xb4D\xd9DI\xe0\x89\r\xbf\v\u0441\xe2\xe7\x00\x00\u07d4]\\\xdb\xe2[*\x04K{\x9b\u30fc\xaaX\a\xb0m<k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]]n\x82\x1cn\uf581\f\x83\u0111F\x85`\xefp\xbf\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]h2K\xcbwm?\xfd\v\xf9\xfe\xa9\x1d\x9f\x03\u007f\u05ab\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]j\xe8\xcb\u05b39<\"\xd1bT\x10\r\x028\xe8\b\x14|\x89'\a\xe5mQ\xa3\f\x00\x00\u07d4]l\\r\rf\xa6\xab\u0283\x97\x14.c\xd2h\x18\xea\xabT\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4]l\u03c0g8\t\x10B\xad\x97\xa6\xe0\x95\xfe\x8c6\xaay\u0149\n1\x06+\xee\xedp\x00\x00\u07d4]qy\x9c\x8d\xf3\xbc\xcb~\xe4F\xdfP\xb81+\xc4\xebq\u0149\n\u05ce\xbcZ\xc6 \x00\x00\u07d4]\x82-\x9b>\xf4\xb5\x02bt\a\xda'/g\x81Jk\xec\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4]\x83\xb2\x1b\xd2q#`Ckg\xa5\x97\xee3x\xdb>z\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94]\x87+\x12.\x99N\xf2|q\xd7\u07b4W\xbfeB\x9e\xcal\x8a\x01\xb1\xad\xed\x81\u04d4\x10\x80\x00\xe0\x94]\x8d1\xfa\xa8d\xe2!Y\xcdoQu\xcc\xec\xc5?\xa5Mr\x8a\x05\xb6\x96\xb7\r\xd5g\x10\x00\x00\xe0\x94]\x95\x8a\x9b\u0449\u0098_\x86\u014a\x8ci\xa7\xa7\x88\x06\xe8\u068a\x02(\xf1o\x86\x15x`\x00\x00\u07d4]\xa2\xa9\xa4\xc2\xc0\xa4\xa9$\xcb\xe0\xa5:\xb9\xd0\xc6'\xa1\u03e0\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4]\xa4\u0288\x93\\'\xf5\\1\x10H\x84\x0eX\x9e\x04\xa8\xa0I\x89\x04V9\x18$O@\x00\x00\u07d4]\xa5G\x85\u027d0W\\\x89\u07b5\x9d A\xd2\n9\xe1{\x89j\xa2\t\xf0\xb9\x1de\x80\x00\xe0\x94]\xb6\x9f\xe9>o\xb6\xfb\xd4P\x96k\x97#\x8b\x11\n\xd8'\x9a\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4]\xb7\xbb\xa1\xf9W?$\x11]\x8c\x8cb\xe9\u0388\x95\x06\x8e\x9f\x89\x02\xb5\xaa\xd7,e \x00\x00\xe0\x94]\xb8D\x00W\x00i\xa9W<\xab\x04\xb4\u6d955\xe2\x02\xb8\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4]\xc3m\xe55\x94P\xa1\xec\t\xcb\fD\xcf+\xb4+:\xe45\x89<\x94m\x89;3\x06\x00\x00\u07d4]\xc6\xf4_\xef&\xb0n3\x021?\x88M\xafH\xe2to\xb9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94]\u0376\xb8zP<m\x8a<e\xc2\u03da\x9a\xa8\x83G\x9a\x1e\x8a\x01\xf2\xbb\xa5\xd8O\x99\xc0\x00\x00\u07d4]\xd1\x12\xf3h\xc0\xe6\xce\xffw\xa9\xdf\x02\xa5H\x16Q\xa0/\xb7\x89\t4r\xc8\\mT\x00\x00\xe0\x94]\xd5:\xe8\x97Rk\x16}9\xf1tN\xf7\xc3\xda[7\xa2\x93\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4]\xde\xd0I\xa6\xe1\xf3)\xdcK\x97\x1er,\x9c\x1f*\u0783\xf0\x8965\u026d\xc5\u07a0\x00\x00\u07d4]\u562b\xa3D7\x8c\xabD1U[Oy\x99-\u0090\u0189Hz\x9a0E9D\x00\x00\u07d4]\xe9\xe7\xd5\u0476g\u0415\xdd4\t\x9c\x85\xb0B\x1a\v\u0181\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4]\xf3'|\xa8Y6\u01e0\xd2\xc0yV\x05\xad%\t^qY\x89lk\x93[\x8b\xbd@\x00\x00\u07d4]\xff\x81\x1d\xad\x81\x9e\xce;\xa6\x02\u00c3\xfb]\xc6L\n:H\x89\n\x15D\xbe\x87\x9e\xa8\x00\x00\u0794^\x03\x1b\nrDq\xd4v\xf3\xbc\xd2\xeb\a\x838\xbfg\xfb\xef\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94^\a\x85S,w#\xe4\xc0\xaf\x93W\xd5'Ks\xbd\xdd\xdd\u078a\x05KA\xea\x9b\xdba\xdc\x00\x00\u07d4^\x11\xec\xf6\x9dU\x1d\u007fO\x84\xdf\x12\x80F\xb3\xa12@\xa3(\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94^\x1f\xbdNX\xe21+<x\u05ea\xaa\xfa\x10\xbf\x9c1\x89\xe3\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94^2\xc7!\x91\xb89,U\xf5\x10\xd8\xe32n:`P\x1db\x8a\tQ>\xa9\xde\x02C\x80\x00\x00\u07d4^Q\xb8\xa3\xbb\t\xd3\x03\xea|\x86\x05\x15\x82\xfd`\x0f\xb3\xdc\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794^X\xe2U\xfc\x19\x87\n\x040_\xf2\xa0F1\xf2\xff)K\xb1\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4^ZD\x19t\xa8=t\u0187\xeb\xdcc?\xb1\xa4\x9e{\x1a\u05c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^eE\x8b\xe9d\xaeD\x9fqw7\x04\x97\x97f\xf8\x89\x87a\x89\x1c\xa7\xccs[o|\x00\x00\u07d4^g\u07c9i\x10\x1a\u06bd\x91\xac\xcdk\xb1\x99\x12t\xaf\x8d\xf2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4^n\x97G\xe1b\xf8\xb4\\en\x0fl\xaez\x84\xba\xc8\x0eN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^s\x1bU\xce\xd4R\xbb??\xe8q\xdd\xc3\xed~\xe6Q\n\x8f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^t\xed\x80\xe9eW\x88\xe1\xbb&\x97R1\x96g\xfeuNZ\x89\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4^w.'\xf2\x88\x00\xc5\r\u0697;\xb3>\x10v.n\xea \x89a\t=|,m8\x00\x00\u07d4^{\x8cT\xdcW\xb0@ bq\x9d\xee~\xf5\xe3~\xa3]b\x89\x9b\xf9\x81\x0f\xd0\\\x84\x00\x00\u07d4^\u007fp7\x87uX\x9f\xc6j\x81\xd3\xf6S\xe9T\xf5U`\ub243\xf2\x89\x18\x1d\x84\xc8\x00\x00\xe0\x94^\x80n\x84W0\xf8\a>l\xc9\x01\x8e\xe9\x0f\\\x05\xf9\t\xa3\x8a\x02\x01\xe9m\xac\u03af \x00\x00\u07d4^\x8eM\xf1\x8c\xf0\xafw\tx\xa8\u07cd\xac\x90\x93\x15\x10\xa6y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4^\x90\xc8Xw\x19\x87V\xb06l\x0e\x17\xb2\x8eR\xb4FPZ\x89\x14JJ\x18\xef\xebh\x00\x00\u07d4^\x95\xfe_\xfc\xf9\x98\xf9\xf9\xac\x0e\x9a\x81\u06b8>\xadw\x00=\x89\x1dB\xc2\r2y\u007f\x00\x00\u07d4^\xad)\x03z\x12\x89dx\xb1)j\xb7\x14\xe9\u02d5B\x8c\x81\x89\x03\xe0C\a-@n\x00\x00\u07d4^\xb3q\xc4\a@lB{;}\xe2q\xad<\x1e\x04&\x95y\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4^\u037a\xea\xb9\x10o\xfe]{Q\x96\x96`\x9a\x05\xba\ub16d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4^\xd0\xd63\x85Y\xefD\xdcza\xed\xeb\x89?\xa5\xd8?\xa1\xb5\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\xe0\x94^\u04fb\xc0R@\xe0\u04d9\xebm\xdf\xe6\x0fb\xdeM\x95\t\xaf\x8a)\x14\xc0$u\xf9\xd6\xd3\x00\x00\u0594^\xd3\xf1\xeb\xe2\xaegV\xb5\xd8\xdc\x19\xca\xd0,A\x9a\xa5w\x8b\x80\u07d4^\xd5a\x15\xbde\x05\xa8\x82s\xdf\\V\x83\x94p\xd2J-\xb7\x89\x03\x8ee\x91\xeeVf\x80\x00\xe0\x94^\xf8\xc9a\x86\xb3y\x84\xcb\xfe\x04\u0158@n;\n\xc3\x17\x1f\x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4^\xfb\xdf\xe58\x99\x99c<&`Z[\xfc,\x1b\xb5\x95\x93\x93\x89\x03\xc0W\xc9\\\xd9\b\x00\x00\xe0\x94_\x13\x15F1Fm\xcb\x13S\u0210\x93*|\x97\xe0\x87\x8e\x90\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4_\x16z\xa2B\xbcL\x18\x9a\xde\xcb:\u0127\xc4R\xcf\x19/\u03c9lkLM\xa6\u077e\x00\x00\xe0\x94_\x1c\x8a\x04\xc9\rs[\x8a\x15)\t\xae\xaeco\xb0\xce\x16e\x8a\x01{x'a\x8cZ7\x00\x00\u07d4_#\xba\x1f7\xa9lE\xbcI\x02YS\x8aT\u008b\xa3\xb0\u0549A\rXj \xa4\xc0\x00\x00\u07d4_&\xcf4Y\x9b\xc3n\xa6{\x9ez\x9f\x9bC0\xc9\xd5B\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4_)\xc9\xdev]\xde%\x85*\xf0}3\xf2\xceF\x8f\xd2\t\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_/\a\xd2\u0597\xe8\xc5g\xfc\xfd\xfe\x02\x0fI\xf3`\xbe!9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_2\x1b=\xaa\xa2\x96\xca\xdf)C\x9f\x9d\xab\x06*K\xff\xed\u0589\x04p%\x90>\xa7\xae\x00\x00\u07d4_3:;#\x10vZ\r\x182\xb9\xbeL\n\x03pL\x1c\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4_4K\x01\xc7\x19\x1a2\xd0v*\xc1\x88\xf0\xec-\xd4`\x91\x1d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94_6>\n\xb7G\xe0-\x1b;f\xab\xb6\x9e\xa5<{\xafR:\x8a\x02w\x01s8\xa3\n\xe0\x00\x00\u07d4_7[\x86`\f@\u0328\xb2gkz\x1a\x1d\x16D\xc5\xf5,\x89\x04F\x18\xd7Lb?\x00\x00\u07d4_>\x1eg9\xb0\xc6\"\x00\xe0\n\x006\x91\xd9\xef\xb28\u061f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_H?\xfb\x8fh\n\xed\xf2\xa3\x8fx3\xaf\xdc\xdeY\xb6\x1eK\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94_J\xceL\x1c\xc13\x91\xe0\x1f\x00\xb1\x98\xe1\xf2\v_\x91\xcb\xf5\x8a\x01\x0f\x0f\xa8\xb9\u04c1\x1a\x00\x00\xe0\x94_R\x12\x82\xe9\xb2x\u070c\x03Lr\xafS\xee)\xe5D=x\x8a\x01as-/\x8f:\xe0\x00\x00\u07d4_h\xa2L~\xb4\x11vgs{39?\xb3\xc2\x14\x8aS\xb6\x89\x02\xce\u0791\x8dE<\x00\x00\u07d4_p\x8e\xaf9\xd8#\x94lQ\xb3\xa3\u9df3\xc0\x03\xe2cA\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4_t.H~:\xb8\x1a\xf2\xf9J\xfd\xbe\x1b\x9b\x8f\\\u0301\xbc\x89u\xc4E\xd4\x11c\xe6\x00\x00\u07d4_t\xed\x0e$\xff\x80\u0672\u0124K\xaa\x99uB\x8c\u05b95\x89\xa1\x8b\xce\xc3H\x88\x10\x00\x00\u07d4_v\xf0\xa3\x06&\x9cx0k=e\r\xc3\xe9\xc3p\x84\xdba\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4_w\xa1\a\xab\x12&\xb3\xf9_\x10\ue0ee\xfcl]\xff>\u0709\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4_{;\xba\xc1m\xab\x83\x1aJ\x0f\xc5;\fT\x9d\xc3l1\u0289i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94_\x93\xff\x83't\xdbQ\x14\xc5[\xb4\xbfD\xcc\U000f53d0?\x8a(\xa9\xc9\x1a&4X)\x00\x00\u07d4_\x96\x16\xc4{Jg\xf4\x06\xb9Z\x14\xfeo\xc2h9o\x17!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4_\x98\x109\xfc\xf5\x02%\xe2\xad\xf7bu!\x12\xd1\xcc&\xb6\xe3\x89\x1b\x1aAj!S\xa5\x00\x00\u07d4_\x99\u070eI\xe6\x1dW\xda\xef`j\xcd\xd9\x1bMp\a2j\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4_\xa6\x1f\x15-\xe6\x125\x16\xc7Q$)y(_yj\u01d1\x89\v\x0f\x11\x97)c\xb0\x00\x00\u07d4_\xa7\xbf\xe0C\x88a'\xd4\x01\x1d\x83V\xa4~\x94yc\xac\xa8\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94_\xa8\xa5Nh\x17lO\xe2\xc0\x1c\xf6q\xc5\x15\xbf\xbd\xd5(\xa8\x8aE\xe1U\xfa\x01\x10\xfa@\x00\x00\u07d4_\xad\x96\x0fk,\x84V\x9c\x9fMG\xbf\x19\x85\xfc\xb2\xc6]\xa6\x8965f3\xeb\xd8\xea\x00\x00\u07d4_\xc6\xc1\x14&\xb4\xa1\xea\xe7\xe5\x1d\xd5\x12\xad\x10\x90\xc6\xf1\xa8[\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4_\u0344Th\x96\xdd\b\x1d\xb1\xa3 \xbdM\x8c\x1d\xd1R\x8cL\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4_\u0368G\xaa\xf8\xd7\xfa\x8b\xca\b\x02\x9c\xa2\x84\x91f\xaa\x15\xa3\x89!\u02b8\x12Y\xa3\xbf\x00\x00\u07d4_\xd1\xc3\xe3\x17x'l\xb4.\xa7@\xf5\xea\xe9\xc6A\xdb\xc7\x01\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4_\xd3\xd6w~\xc2b\n\xe8:\x05R\x8e\xd4%\a-<\xa8\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4_\xd9s\xaf6j\xa5\x15|Te\x9b\u03f2|\xbf\xa5\xac\x15\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4_\xe7w\x03\x80\x8f\x82>l9\x93R\x10\x8b\xdb,R|\xb8|\x89j@v\xcfy\x95\xa0\x00\x00\xe0\x94_\xecI\xc6e\xe6N\xe8\x9d\xd4A\xeet\x05n\x1f\x01\xe9(p\x8a\x01V\x9b\x9es4t\xc0\x00\x00\u07d4_\xf3&\xcd`\xfd\x13k$^)\xe9\bzj\u04e6R\u007f\r\x89e\xea=\xb7UF`\x00\x00\u07d4_\xf9=\xe6\xee\x05L\xadE\x9b-^\xb0\xf6\x87\x03\x89\xdf\xcbt\x89\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4`\x06\xe3m\x92\x9b\xf4]\x8f\x16#\x1b\x12j\x01\x1a\xe2\x83\xd9%\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4`!\xe8Z\x88\x14\xfc\xe1\xe8*A\xab\xd1\u04f2\xda\xd2\xfa\xef\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4`8t\n\xe2\x8df\xba\x93\xb0\xbe\bH+2\x05\xa0\xf7\xa0{\x89\x11!a\x85\u009fp\x00\x00\u07d4`?/\xabz\xfbn\x01{\x94v`i\xa4\xb4;8\x96I#\x89Y\xd2\xdb$\x14\u0699\x00\x00\u07d4`B'm\xf2\x98?\xe2\xbcGY\xdc\x19C\xe1\x8f\xdb\xc3Ow\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4`B\xc6D\xba\xe2\xb9o%\xf9M1\xf6x\xc9\r\xc9f\x90\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4`L\xdf\x18b\x8d\xbf\xa82\x91\x94\xd4x\xddR\x01\xee\xccK\xe7\x89\x01?0j$\t\xfc\x00\x00\u07d4`N\x94w\xeb\xf4r|t[\u02bb\xed\xcbl\xcf)\x99@\"\x8966\x9e\xd7t}&\x00\x00\u07d4`gm\x1f\xa2\x1f\xca\x05\"\x97\xe2K\xf9c\x89\u0171*p\u05c9\r\x17|Zzh\xd6\x00\x00\u07d4`gn\x92\u044b\x00\x05\t\xc6\x1d\xe5@\xe6\xc5\u0776v\xd5\t\x89A\rXj \xa4\xc0\x00\x00\u07d4`o\x17q!\xf7\x85\\!\xa5\x06#0\xc8v\"d\xa9{1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\x86B6\x93\r\x04\xd8@+]\xcb\xeb\x80\u007f<\xafa\x1e\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4`\xabq\xcd&\xeamnY\xa7\xa0\xf6'\xee\a\x9c\x88^\xbb\xf6\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4`\xaf\x0e\xe1\x18D<\x9b7\xd2\xfe\xadw\xf5\xe5!\u07be\x15s\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4`\xb3X\xcb=\xbe\xfa7\xf4}\xf2\xd76X@\u068e;\u024c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4`\xb8\u05b7;ySO\xb0\x8b\xb8\xcb\xce\xfa\xc7\xf3\x93\xc5{\xfe\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4`\xbeo\x95?*M%\xb6%o\xfd$#\xac\x148%.N\x89\b!\xab\rD\x14\x98\x00\x00\u0794`\xc3qO\xdd\xdbcFY\u48b1\xeaB\xc4r\x8c\u01f8\xba\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4`\xcc=D^\xbd\xf7j}z\xe5q\u0197\x1d\xffh\u0305\x85\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94`\xd5fq@\xd1&\x14\xb2\x1c\x8e^\x8a3\b.2\xdf\xcf#\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4`\xde\"\xa1Pt2\xa4{\x01\xcch\xc5*\v\xf8\xa2\xe0\u0418\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4`\xe0\xbd\u0422Y\xbb\x9c\xb0\x9d?7\xe5\u034b\x9d\xac\uafca\x89JD\x91\xbdm\xcd(\x00\x00\u07d4`\xe3\xccC\xbc\xdb\x02j\xadu\x9cpf\xf5U\xbb\xf2\xacf\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x04+\x80\xfd`\x95\u0478{\xe2\xf0\x0f\x10\x9f\xab\xaf\xd1W\xa6\x89\x05k\xc7^-c\x10\x00\x00\u07d4a\a\xd7\x1d\xd6\xd0\xee\xfb\x11\xd4\xc9\x16@L\xb9\x8cu>\x11}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\x0f\xd6\xeeN\xeb\xab\x10\xa8\xc5]\vK\xd2\xe7\xd6\xef\x81qV\x89\x01\x15\x95a\x06]]\x00\x00\u07d4a\x14\xb0\xea\xe5Wi\x03\xf8\v\xfb\x98\x84-$\xed\x92#\u007f\x1e\x89\x05k\xc7^-c\x10\x00\x00\u07d4a!\xaf9\x8a[-\xa6\x9fe\xc68\x1a\xec\x88\u039c\xc6D\x1f\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\u07d4a&g\xf1r\x13[\x95\v,\xd1\xde\x10\xaf\xde\xcehW\xb8s\x8965\u026d\xc5\u07a0\x00\x00\u07d4a,\xed\x8d\xc0\u071e\x89\x9e\xe4oyb33\x15\xf3\xf5^D\x89\x12^5\xf9\xcd=\x9b\x00\x00\u07d4a4\xd9B\xf07\xf2\xcc=BJ#\f`=g\xab\xd3\xed\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a:\xc5;\xe5e\xd4e6\xb8 q[\x9b\x8d:\xe6\x8aK\x95\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\u07d4a?\xabD\xb1k\xbeUMD\xaf\xd1x\xab\x1d\x02\xf3z\ua949lk\x93[\x8b\xbd@\x00\x00\u07d4aN\x8b\xef=\xd2\u015bY\xa4\x14Vt@\x10\x185\x18\x84\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4aQ\x84d\xfd\u0637<\x1b\xb6\xacm\xb6\x00eI8\xdb\xf1z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4aT}7nSi\xbc\xf9x\xfc\x16,<V\xaeE5G\xe8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4aX\xe1\a\xc5\xebT\xcbv\x04\xe0\u034d\xc1\xe0u\x00\xd9\x1c<\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94aZo6w\u007f@\xd6a~\xb5\x81\x98\x96\x18i\x83\xfd71\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4a_\x826\\Q\x01\xf0q\xe7\xd2\xcbj\xf1Oz\xad,\x16\u0189\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4ap\xdd\x06\x87\xbdU\u0288\xb8z\xde\xf5\x1c\xfd\xc5\\M\xd4X\x89l\xb3/\\4\xfeD\x00\x00\u07d4as9G\xfa\xb8 \xdb\xd3Q\xef\xd6xU\xea\x0e\x88\x13s\xa0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4ay\x97\x99\a\xfe\u007f\x03~L8\x02\x9d`\xbc\xba\xb82\xb3\u0589WG=\x05\u06ba\xe8\x00\x00\xe0\x94a\u007f \x89O\xa7\x0e\x94\xa8jI\xcdt\xe028\xf6M<\u064a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4a\u007f\xf2\u0300>1\xc9\b\"3\xb8%\xd0%\xbe?{\x10V\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4a\x91\xdd\u0276J\x8e\b\x90\xb427\t\u05e0|H\xb9*d\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4a\x96\xc3\xd3\xc0\x90\x8d%Cf\xb7\xbc\xa5WE\"-\x9dM\xb1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\x9f\x17\x14E\xd4+\x02\xe2\xe0p\x04\xad\x8a\xfeiO\xa5=j\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4a\xad\xf5\x92\x9a^)\x81hN\xa2C\xba\xa0\x1f}\x1f^\x14\x8a\x89\x05\xfa\xbfl\x98O#\x00\x00\u07d4a\xb1\xb8\xc0\x12\xcdLx\xf6\x98\xe4p\xf9\x02V\xe6\xa3\x0fH\u0749\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xb3\xdf.\x9e\x9f\xd9h\x13\x1f\x1e\x88\xf0\xa0\xeb[\xd7eFM\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4a\xb9\x02\u0166s\x88X&\x82\r\x1f\xe1EI\xe4\x86_\xbd\u0089\x12$\xef\xed*\u1440\x00\u07d4a\xb9\x05\xdef?\xc1s\x86R;:(\xe2\xf7\xd07\xa6U\u0349\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xba\x87\xc7~\x9bYm\xe7\xba\x0e2o\xdd\xfe\xec!c\xeff\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4a\xbf\x84\u056b\x02oX\xc8s\xf8o\xf0\xdf\u0282\xb5W3\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xc4\xee|\x86LMk^7\xea\x131\xc2\x03s\x9e\x82k/\x89\x01\xa15;8*\x91\x80\x00\u07d4a\xc80\xf1eG\x18\xf0u\u032b\xa3\x16\xfa\xac\xb8[}\x12\v\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4a\xc8\xf1\xfaC\xbf\x84i\x99\xec\xf4{+2M\xfbkc\xfe:\x89+^:\xf1k\x18\x80\x00\x00\u07d4a\xc9\xdc\u8c98\x1c\xb4\x0e\x98\xb0@+\xc3\xeb(4\x8f\x03\xac\x89\n\xac\xac\u0679\xe2+\x00\x00\u07d4a\u03a7\x1f\xa4d\xd6*\a\x06?\x92\v\f\xc9\x17S\x973\u0609Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4a\xd1\x01\xa03\xee\x0e.\xbb1\x00\xed\xe7f\xdf\x1a\xd0$IT\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4a\xedU\x96\u0197 \u007f=U\xb2\xa5\x1a\xa7\xd5\x0f\a\xfa\t\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4a\xff\x8eg\xb3M\x9e\xe6\xf7\x8e\xb3o\xfe\xa1\xb9\xf7\xc1W\x87\xaf\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4b\x05\xc2\xd5dtp\x84\x8a8@\xf3\x88~\x9b\x01]4u\\\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4b(\xad\xe9^\x8b\xb1}\x1a\xe2;\xfb\x05\x18AMI~\x0e\xb8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94b)\xdc\xc2\x03\xb1\xed\xcc\xfd\xf0n\x87\x91\fE*\x1fMzr\x8a\x06\xe1\xd4\x1a\x8f\x9e\xc3P\x00\x00\u0794b+\xe4\xb4T\x95\xfc\xd91C\xef\xc4\x12\u0599\xd6\xcd\xc2=\u0148\xf0\x15\xf2W6B\x00\x00\u07d4b3\x1d\xf2\xa3\xcb\xee5 \xe9\x11\u07a9\xf7>\x90_\x89%\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4bVD\xc9Z\x87>\xf8\xc0l\u06de\x9fm\x8dv\x80\x04=b\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4be\xb2\xe7s\x0f6\xb7v\xb5-\f\x9d\x02\xad\xa5]\x8e<\xb6\x8965\u026d\xc5\u07a0\x00\x00\u07d4bh\n\x15\xf8\u0338\xbd\xc0/s`\xc2Z\xd8\u03f5{\x8c\u034965\u026d\xc5\u07a0\x00\x00\u07d4b\x94\xea\xe6\xe4 \xa3\xd5`\n9\xc4\x14\x1f\x83\x8f\xf8\xe7\xccH\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4b\x97\x1b\xf2cL\xee\v\xe3\u0249\x0fQ\xa5`\x99\u06f9Q\x9b\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4b\x9b\xe7\xab\x12jS\x98\xed\xd6\u069f\x18D~x\u0192\xa4\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xb4\xa9\"nah<r\xc1\x83%F\x90\xda\xf5\x11\xb4\x11z\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4b\xb9\b\x1ew\x104^8\xe0.\x16D\x9a\xce\x1b\x85\xbc\xfcN\x891T\xc9r\x9d\x05x\x00\x00\u07d4b\xc3|R\xb9\u007fK\x04\v\x1a\xa3\x91\xd6\xde\xc1R\x89<G\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4b\u0272q\xff\u0577p\xa5\xee\xe4\xed\xc9x{\\\xdcp\x97\x14\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xd5\xccq\x17\xe1\x85\x00\xac/\x9e<&\xc8k\n\x94\xb0\xde\x15\x89\x05\xb1*\ufbe8\x04\x00\x00\u07d4b\xdcrr\x90$7_\xc3|\xbb\x9c|#\x93\xd1\x0233\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4b\xe6\xb2\xf5\xeb\x94\xfazC\x83\x1f\xc8~%J?\u3fcf\x89\x89\r\x8drkqw\xa8\x00\x00\u07d4b\xf2\xe5\xcc\xec\xd5,\u0139^\x05\x97\xdf'\xcc\a\x97\x15`\x8c\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94b\xfb\x8b\xd1\xf0\xe6k\x90S>\a\x1el\xbea\x11\xfe\xf0\xbcc\x8a\x03\xba\x19\x10\xbf4\x1b\x00\x00\x00\xe0\x94c\n\x91:\x901\xc9I*\xbdLA\u06f1PT\xcf\xecD\x16\x8a\x014X\xdbg\xaf5\xe0\x00\x00\xe0\x94c\fRs\x12mQ|\xe6q\x01\x81\x1c\xab\x16\xb8SL\xf9\xa8\x8a\x01\xfe\xcc\xc6%s\xbb\u04c0\x00\u07d4c\x100\xa5\xb2{\a(\x8aEio\x18\x9e\x11\x14\xf1*\x81\xc0\x89\x1b\x1azB\v\xa0\r\x00\x00\u07d4c\x10\xb0 \xfd\x98\x04IW\x99P\x92\t\x0f\x17\xf0NR\xcd\xfd\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4c+\x91I\xd7\x01x\xa7364'^\x82\u0555?'\x96{\x89%\xf2s\x93=\xb5p\x00\x00\u07d4c,\xec\xb1\f\xfc\xf3\x8e\u0246\xb4;\x87p\xad\xec\xe9 \x02!\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c1\x02\x8c\xbbZ!H[\xc5\x1bVQB\x99;\xdb%\x82\xa9\x89\x1c\xfd\xd7F\x82\x16\xe8\x00\x00\u07d4c3O\xcf\x17E\x84\x0eK\tJ;\xb4\v\xb7o\x96\x04\xc0L\x89\u05e5\xd7\x03\xa7\x17\xe8\x00\x00\u07d4c4\nWqk\xfac\xebl\xd13r\x12\x02W[\xf7\x96\xf0\x89\va\xe0\xa2\f\x12q\x80\x00\u07d4cN\xfc$7\x11\a\xb4\xcb\xf0?y\xa9=\xfd\x93\xe41\xd5\xfd\x89B5\x82\xe0\x8e\xdc\\\x80\x00\xe0\x94c\\\x00\xfd\xf05\xbc\xa1_\xa3a\r\xf38N\x0f\xb7\x90h\xb1\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4ca.xb\xc2{X|\xfbm\xaf\x99\x12\xcb\x05\x1f\x03\n\x9f\x89\x02[\x19\u053f\xe8\xed\x00\x00\u07d4cfgU\xbdA\xb5\x98i\x97x<\x13\x040\b$+<\xb5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4c{\xe7\x1b:\xa8\x15\xffE=VB\xf70tE\vd\xc8*\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94c}g\xd8\u007fXo\nZG\x9e \xee\x13\xea1\n\x10\xb6G\x8a\n:Y&\xaf\xa1\xe70\x00\x00\u07d4c\u007fXi\xd6\xe4i_\x0e\xb9\xe2s\x11\u0107\x8a\xff33\x80\x89j\xc0Nh\xaa\xec\x86\x00\x00\u07d4c\x97|\xad}\r\xcd\xc5+\x9a\xc9\xf2\xff\xa16\xe8d(\x82\xb8\x89\x04\x10\u0546\xa2\nL\x00\x00\u07d4c\xa6\x1d\xc3\n\x8e;0\xa7c\xc4!<\x80\x1c\xbf\x98s\x81x\x8965\u026d\xc5\u07a0\x00\x00\u07d4c\xacT\\\x99\x12C\xfa\x18\xae\xc4\x1dOoY\x8eUP\x15\u0709 \x86\xac5\x10R`\x00\x00\u07d4c\xb9uMu\xd1-8@9\xeci\x06<\v\xe2\x10\xd5\xe0\u3252\v\x86\f\xc8\xec\xfd\x80\x00\u07d4c\xbbfO\x91\x17\x03v(YM\xa7\xe3\xc5\b\x9f\xd6\x18\xb5\xb5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4c\u00a3\xd25\xe5\xee\xab\xd0\u0526\xaf\u06c9\xd9F'9d\x95\x89CN\xf0[\x9d\x84\x82\x00\x00\u07d4c\xc8\xdf\xde\v\x8e\x01\xda\xdc.t\x8c\x82L\xc06\x9d\U00010cc9\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4c\xd5Z\u065b\x917\xfd\x1b \xcc+O\x03\xd4,\xba\xdd\xf34\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4c\xd8\x00H\x87u\x96\xe0\u0084\x89\xe6P\xcdJ\xc1\x80\tjI\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94c\xe4\x14`>\x80\xd4\xe5\xa0\xf5\xc1\x87t FB%\x82\b\xe4\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94c\xe8\x8e.S\x9f\xfbE\x03\x86\xb4\xe4g\x89\xb2#\xf5GlE\x8a\x01U\x17\nw\x8e%\xd0\x00\x00\u07d4c\xef/\xbc=\xaf^\xda\xf4\xa2\x95b\x9c\xcf1\xbc\xdf@8\xe5\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4c\xf0\xe5\xa7R\xf7\x9fg\x12N\xedc:\xd3\xfd'\x05\xa3\x97\u0509\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94c\xf5\xb5=y\xbf.A\x14\x89Re0\"8E\xfa\xc6\xf6\x01\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4c\xfc\x93\x00\x13\x05\xad\xfb\u0278])\xd9)\x1a\x05\xf8\xf1A\v\x8965\u026d\xc5\u07a0\x00\x00\u0794c\xfek\xccK\x8a\x98P\xab\xbeu\x8070\xc92%\x1f\x14[\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4d\x03\xd0bT\x96\x90\xc8\xe8\xb6>\xaeA\xd6\xc1\tGn%\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4d\x04+\xa6\x8b\x12\xd4\xc1Qe\x1c\xa2\x81;sR\xbdV\xf0\x8e\x89 \x86\xac5\x10R`\x00\x00\u0794d\x05\xdd\x13\xe9:\xbc\xff7~p\x0e<\x1a\x00\x86\xec\xa2})\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94d\n\xbam\xe9\x84\xd9E\x177x\x03p^\xae\xa7\t_J\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4d\v\xf8t\x15\xe0\xcf@s\x01\xe5Y\x9ah6m\xa0\x9b\xba\u0209\x1a\xbc\x9fA`\x98\x15\x80\x00\u07d4d \xf8\xbc\xc8\x16JaR\xa9\x9dk\x99i0\x05\xcc\xf7\xe0S\x8965f3\xeb\xd8\xea\x00\x00\u07d4d$\x1axD)\x0e\n\xb8U\xf1\u052au\xb5SE\x03\"$\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4d&J\xed\xd5-\xca\xe9\x18\xa0\x12\xfb\xcd\f\x03\x0e\xe6\xf7\x18!\x8965\u026d\xc5\u07a0\x00\x00\u07d4d7\x0e\x87 &E\x12Z5\xb2\a\xaf\x121\xfb`r\xf9\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4d=\x9a\xee\u0531\x80\x94~\u04b9 |\xceL=\xdcU\xe1\xf7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4dC\xb8\xaec\x9d\xe9\x1c\xf7<Z\xe7c\xee\xee\xd3\u077b\x92S\x89lk\x93[\x8b\xbd@\x00\x00\u07d4dE\u007f\xa3;\b2PlO}\x11\x80\xdc\xe4\x8fF\xf3\xe0\xff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4dFJh\x05\xb4bA*\x90\x1d-\xb8\x17K\x06\xc2-\ue989\x19\xc8F\xa0)\xc7\xc8\x00\x00\u07d4dK\xa6\xc6\x10\x82\xe9\x89\x10\x9f\\\x11\u0534\x0e\x99\x16`\xd4\x03\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4db\x8co\xb8\xect:\xdb\xd8|\xe5\xe0\x18\xd51\xd9!\x047\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4dc\xf7\x15\u0554\xa1\xa4\xac\u4edc;(\x8at\xde\xcf)M\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4df(\xa5<,A\x93\u06885\x9c\xe7\x18\xda\u0752\xb7\xa4\x8d\x89\n\xd8\x00l/^\xf0\x00\x00\u07d4dg-\xa3\xab\x05(!\xa0$=\x1c\xe4\xb6\xe0\xa3e\x17\xb8\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4dj\xfb\xa7\x1d\x84\x9e\x80\xc0\xedY\xca\xc5\x19\xb2x\xe7\xf7\xab\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4dn\x04=\x05\x97\xa6d\x94\x8f\xbb\r\xc1Tu\xa3\xa4\xf3\xa6\xed\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4dp\xa4\xf9.\u01b0\xfc\xcd\x01#O\xa5\x90#\xe9\xff\x1f:\xac\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4d{\x85\x04M\xf2\xcf\vN\u0508.\x88\x81\x9f\xe2*\xe5\xf7\x93\x8966;]\x9awp\x00\x00\u07d4d\x85G\x0ea\xdb\x11\n\xeb\u06ef\xd56v\x9e<Y\x9c\xc9\b\x89 \x86\xac5\x10R`\x00\x00\xe0\x94d\x8f[\u04a2\xae\x89\x02\xdb7\x84}\x1c\xb0\u06d3\x90\xb0bH\x8a\x01\xa55\xec\xf0v\n\x04\x80\x00\xe0\x94d\x9a+\x98y\u034f\xb76\xe6p;\fwG\x84\x97\x96\xf1\x0f\x8a\x01\x8e\xe2-\xa0\x1a\xd3O\x00\x00\u07d4d\x9a\x85\xb96S\a_\xa6V,@\x9aV]\b{\xa3\u1e89lk\x93[\x8b\xbd@\x00\x00\u07d4d\xad\xcc\xee\xc5=\xd9\xd9\xdd\x15\xc8\xcc\x1a\x9esm\xe4$\x1d,\x89\x03\t'\xf7L\x9d\xe0\x00\x00\u07d4d\xcf\t5\xbf\x19\xd2\u03bb\xec\xd8x\r'\xd2\xe2\xb2\xc3Af\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4d\xd8\f;\x8b\xa6\x82\x82)\vu\xe6]\x89x\xa1Z\x87x,\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94d\u06e2\xd6a[\x8b\xd7W\x186\xdcu\xbcy\xd3\x14\xf5\xec\xee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4d\xe0!z[8\xaa@X6%\x96\u007f\xa9\x886\x908\x8bo\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4d\xe0*\xbb\x01l\xc2:)4\xf6\xbc\u0776\x81\x90P!\xd5c\x8965\u026d\xc5\u07a0\x00\x00\u07d4d\xe0>\xf0p\xa5G\x03\xb7\x18NH'l\\\x00w\xefK4\x89\x11X\xe4`\x91=\x00\x00\x00\xe0\x94d\xe2\xde! \v\x18\x99\u00e0\xc0e;P@\x13m\r\xc8B\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4d\xec\x8a[t?4y\xe7\a\xda\xe9\xee \u076aO@\xf1\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e\x03\x86\v\x19\x10\b\xc1U\x83\xbf\u0201X\t\x93\x01v((\x8965\u026d\xc5\u07a0\x00\x00\u07d4e\x051\x911\x9e\x06z%\xe66\x1dG\xf3\u007fc\x18\xf84\x19\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4e\t;#\x9b\xbf\xba#\xc7w\\\xa7\xdaZ\x86H\xa9\xf5L\xf7\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4e\t\xee\xb14~\x84/\xfbA>7\x15^,\xbcs\x82s\xfd\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94e\vBUU\xe4\xe4\xc5\x17\x18\x14h6\xa2\xc1\xeew\xa5\xb4!\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4e\f\xf6}\xb0`\xcc\xe1uh\xd5\xf2\xa4#h|Idv\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4e\x10\xdfB\xa5\x99\xbc\xb0\xa5\x19\u0329a\xb4\x88u\x9aogw\x89lk\x93[\x8b\xbd@\x00\x00\u07d4e6u\xb8B\xd7\u0634a\xf7\"\xb4\x11|\xb8\x1d\xac\x8ec\x9d\x89\x01\xae6\x1f\xc1E\x1c\x00\x00\u07d4eK~\x80\x87\x99\xa8=r\x87\xc6w\x06\xf2\xab\xf4\x9aId\x04\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94eORHG\xb3\xa6\xac\xc0\xd3\xd5\xf1\xf3b\xb6\x03\xed\xf6_\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4eY4\u068etN\xaa=\xe3M\xbb\xc0\x89LN\xda\va\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e]\\\xd7H\x96)\xe2A<!\x05\xb5\xa1r\xd93\xc2z\xf8\x89\xdb\x03\x18l\xd8@\xa6\x00\x00\u07d4e`\x18XA0\u06c3\xab\x05\x91\xa8\x12\x8d\x93\x81fj\x8d\x0e\x89\x03w\x9f\x91 \x19\xfc\x00\x00\u07d4e`\x94\x13(\xffX|\xbcV\u00ccx#\x8a{\xb5\xf4B\xf6\x89(a\x90kY\xc4z\x00\x00\u07d4eey\xda\xed\u0493p\u06777\xee?\\\xd9\xd8K\u00b3B\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4etswOc\xac=by\xfd\aC\xd5y\fO\x16\x15\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4e\x80\xb1\xbc\x949\x0f\x04\xb3\x97\xbds\xe9]\x96\xef\x11\xea\xf3\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4e\x84\x9b\xe1\xaf \x10\x0e\xb8\xa3\xbaZ[\xe4\u04ee\x8d\xb5\xa7\x0e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4e\x9c\nr\xc7g\xa3\xa6\\\xed\x0e\x1c\xa8\x85\xa4\xc5\x1f\u0677y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4e\xa5!A\xf5k\uf619\x17$\xc6\xe7\x053\x81\u068bY%\x89\x03B\x9c3]W\xfe\x00\x00\u07d4e\xa9\xda\xd4.\x162\xba>NIb?\xabb\xa1~M6\x11\x89\x05\fL\xb2\xa1\f`\x00\x00\u07d4e\xaf\x8d\x8b[\x1d\x1e\xed\xfaw\xbc\xbc\x96\xc1\xb13\xf83\x06\u07c9\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4e\xaf\x90\x87\xe0QgqT\x97\u0265\xa7I\x18\x94\x89\x00M\xef\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u0794e\xb4/\xae\xcc\x1e\u07f1B\x83\u0297\x9a\xf5E\xf6;0\xe6\f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u0794e\xd3>\xb3\x9c\xdadS\xb1\x9ea\xc1\xfeM\xb91p\xef\x9d4\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4e\xd8\xddN%\x1c\xbc\x02\x1f\x05\xb0\x10\xf2\xd5\xdcR\f8r\xe0\x89-CW\x9a6\xa9\x0e\x00\x00\u07d4e\xea&\xea\xbb\xe2\xf6L\xcc\xcf\xe0h)\xc2]F7R\x02%\x89%\xf2s\x93=\xb5p\x00\x00\u07d4e\xeag\xad?\xb5j\xd5\xfb\x948}\u04ce\xb3\x83\x00\x1d|h\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94e\xeb\xae\xd2~\u06dd\xcc\x19W\xae\xe5\xf4R\xac!\x05\xa6\\\x0e\x8a\t7\u07ed\xae%\u26c0\x00\u07d4e\xee \xb0m\x9a\u0549\xa7\xe7\xce\x04\xb9\xf5\xf7\x95\xf4\x02\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4e\xf544m/\xfbx\u007f\xa9\xcf\x18]t[\xa4)\x86\xbdn\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94e\xf5\x87\x0f&\xbc\xe0\x89g}\xfc#\xb5\x00\x1e\xe4\x92H4(\x8a\x01\x12\xb1\xf1U\xaa2\xa3\x00\x00\u07d4e\xfd\x02\xd7\x04\xa1*M\xac\xe9G\x1b\x06E\xf9b\xa8\x96q\u0209\x01\x8d\x1c\xe6\xe4'\u0340\x00\u07d4e\xff\x87O\xaf\xceM\xa3\x18\xd6\xc9=W\xe2\u00ca\rs\xe8 \x8968\x02\x1c\xec\u06b0\x00\x00\xe0\x94f\x05W\xbbC\xf4\xbe:\x1b\x8b\x85\xe7\xdf{<[\xcdT\x80W\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4f\b,u\xa8\xde1\xa59\x13\xbb\xd4M\xe3\xa07O\u007f\xaaA\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4f\x11\xceY\xa9\x8b\a*\xe9Y\xdcI\xadQ\x1d\xaa\xaa\xa1\x9dk\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4f \x1b\xd2'\xaem\u01bd\xfe\xd5\xfb\u0781\x1f\xec\xfe^\x9d\u0649 >\x9e\x84\x92x\x8c\x00\x00\u07d4f#4\x81G$\x93[y1\xdd\xcaa\x00\xe0\rFw'\u0349\"\x88&\x9d\a\x83\xd4\x00\x00\u07d4f'O\xea\x82\xcd0\xb6\u009b#5\x0eOO=1\nX\x99\x89p7\x05P\xab\x82\x98\x00\x00\u07d4f,\xfa\x03\x8f\xab7\xa0\x17E\xa3d\u1e41'\xc5\x03tm\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4f5\xb4oq\x1d-\xa6\xf0\xe1cp\u034e\xe4>\xfb,-R\x89lk\x93[\x8b\xbd@\x00\x00\u07d4f6\x04\xb0P0F\xe6$\xcd&\xa8\xb6\xfbGB\xdc\xe0*o\x89\x03\x8b\x9by~\xf6\x8c\x00\x00\u07d4f6\u05ecczH\xf6\x1d8\xb1L\xfdHe\xd3m\x14(\x05\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f@\xcc\xf0SU\\\x13\n\xe2\xb6Vd~\xa6\xe3\x167\xb9\xab\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4fBK\xd8x[\x8c\xb4a\x10*\x90\x02\x83\xc3]\xfa\a\xefj\x89\x02.-\xb2ff\xfc\x80\x00\u07d4fL\xd6}\xcc\u026c\x82(\xb4\\U\u06cdvU\ve\x9c\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4fNC\x11\x98p\xaf\x10zD\x8d\xb1'\x8b\x04H8\xff\u036f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4fQso\xb5\x9b\x91\xfe\xe9\xc9:\xa0\xbdn\xa2\xf7\xb2Pa\x80\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4f[\x00\x0f\vw'P\xcc<!z^\xf4)\xa9+\xf1\u033b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4ff \x06\x01\\\x1f\x8e<\xcf\xca\xeb\xc8\xeeh\a\xee\x19c\x03\x89\x1b\x1b:\x1a\xc2a\xec\x00\x00\u07d4fgF\xfb\x93\u0453\\Z<hNrP\x10\xc4\xfa\u0431\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4fkO7\xd5]c\xb7\xd0V\xb6\x15\xbbt\xc9k;\x01\x99\x1a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4fq\x9c\x06\x82\xb2\xac\u007f\x9e'\xab\xeb\xec~\u07cd\xec\xf0\xae\r\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4fq\xb1\x82\xc9\xf7A\xa0\xcd<5ls\xc21&\xd4\xf9\xe6\xf4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4fy\xae\xec\xd8zW\xa7?3V\x81\x1d,\xf4\x9d\fM\x96\u0709 \x86\xac5\x10R`\x00\x00\u07d4f{a\xc0;\xb97\xa9\xf5\xd0\xfcZ\t\xf1\xea3c\xc7p5\x89\xe6d\x99\"\x88\xf2(\x00\x00\u07d4f\x85\xfd.%Dp,6\v\x8b\xb9\xeex\xf10\xda\xd1m\xa5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94f\x8bk\xa8\xab\b\xea\xce9\xc5\x02\xefg+\xd5\u0336\xa6z \x8a\x06\x97\xd9]B\x013<\x00\x00\u07d4f\x92]\xe3\xe4?KA\xbf\x9d\xad\xde'\xd5H\x8e\xf5i\xea\r\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4f\xb0\xc1\x00\u0111I\x93]\x14\xc0\xdc ,\u0390|\xea\x1a=\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4f\xb1\xa6=\xa4\xdc\xd9\xf8\x1f\xe5O^?\xcb@U\xef~\xc5O\x89\n\xeb'*\u07dc\xfa\x00\x00\u07d4f\xb3\x987\xcb<\xac\x8a\x80*\xfe?\x12\xa2X\xbb\xcab\xda\u0349\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94f\xc83\x1e\xfeq\x98\xe9\x8b-2\xb98h\x8e2A\xd0\xe2O\x8a\x02\t\x80Q\x97\x0e9\xd0\x00\x00\u07d4f\u030a\xb2<\x00\u0478*\xcd}s\xf3\x8c\x99\xe0\xd0ZO\xa6\x89\x05k\xc7^-c\x10\x00\x00\u07d4f\xdc\xc5\xfbN\xe7\xfe\xe0F\xe1A\x81\x9a\xa9hy\x9ddD\x91\x89Hz\x9a0E9D\x00\x00\u07d4f\xe0\x94'\xc1\xe6=\xee\xd7\xe1+\x8cU\xa6\xa1\x93 \xefKj\x89\t79SM(h\x00\x00\u07d4f\xec\x16\ue72a\xb4\x11\xc5Zf)\xe3\x18\xden\xe2\x16I\x1d\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4f\xf5\x04\x06\xeb\x1b\x11\xa9F\u02b4Y'\u0323tp\xe5\xa2\b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94f\xfd\xc9\xfe\xe3Q\xfa\x158\xeb\r\x87\xd8\x19\xfc\xf0\x9e|\x10j\x8a\x01F'\xb5\xd97\x81\xb2\x00\x00\u07d4g\x04\x8f:\x12\xa4\xdd\x1fbld&L\xb1\u05d7\x1d\xe2\xca8\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4g\x04\xf1i\xe0\u0433kW\xbb\u00df<EC{^\xe3\u048d\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4g\x10\x15\xb9vp\xb1\r^X?=b\xa6\x1c\x1cy\xc5\x14?\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94g\x10\xc2\xc0<e\x99+.wK\xe5-:\xb4\xa6\xba!~\xf7\x8a\x02t\xd6V\xac\x90\xe3@\x00\x00\u07d4g\x11\x10\xd9j\xaf\xf1\x15#\xccTk\xf9\x94\x0e\xed\xff\xb2\xfa\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4g\x15\xc1@5\xfbW\xbb=f\u007f{pt\x98\xc4\x10t\xb8U\x89%\xf2s\x93=\xb5p\x00\x00\u07d4g\x1b\xbc\xa0\x99\xff\x89\x9b\xab\a\xea\x1c\xf8ie\xc3\x05L\x89`\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4g'\xda\xf5\xb9\u058e\xfc\xabH\x9f\xed\xec\x96\xd7\xf72]\xd4#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4g,\xbc\xa8D\n\x85w\t{\x19\xaf\xf5\x93\xa2\xad\x9d(\xa7V\x89\x04V9\x18$O@\x00\x00\xe0\x94g.\xc4/\xaa\x8c\u059a\xaaq\xb3,\u01f4\x04\x88\x1dR\xff\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94g/\xa0\xa0\x19\b\x8d\xb3\x16oa\x19C\x8d\a\xa9\x9f\x8b\xa2$\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\u07d4g1D\xf0\xec\x14.w\x0fH4\xfe\xe0\xee1\x182\xf3\b{\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4g5\vS1\x92o^(\xf3\xc1\xe9\x86\xf9dC\x80\x9c\x8b\x8c\x89\x13\x14\xfb7\x06)\x80\x00\x00\xe0\x94g7\x06\xb1\xb0\xe4\xdcz\x94\x9azybX\xa5\xb8;\xb5\xaa\x83\x8a\x03h\xc8b:\x8bM\x10\x00\x00\u07d4gB\xa2\xcf\u038dy\xa2\u0125\x1bwtt\x98\x91\"E\xcdj\x89\r\xfd[\x80\xb7\xe4h\x00\x00\u07d4gJ\xdb!\xdfL\x98\u01e3G\xacL<$&gW\xddp9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4gQ\x8e]\x02\xb2\x05\x18\x0f\x04c\xa3 \x04G\x1fu<R>\x89k\x91\x8a\xacIK\x16\x80\x00\xe0\x94g]\\\xaa`\x9b\xf7\n\x18\xac\xa5\x80F]\x8f\xb71\r\x1b\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4gc F\u0732ZT\x93i(\xa9oB?3 \xcb\ud489lk\x93[\x8b\xbd@\x00\x00\u07d4ge\xdf%(\x0e\x8eO8\u0531\xcfDo\xc5\xd7\xebe\x9e4\x89\x05k\xc7^-c\x10\x00\x00\u07d4gv\xe13\xd9\xdc5L\x12\xa9Q\b{c\x96P\xf59\xa43\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4g\x85Q<\xf72\xe4~\x87g\ap\xb5A\x9b\xe1\f\xd1\xfct\x89lk\x93[\x8b\xbd@\x00\x00\u07d4g\x947\xea\xcfCxx\xdc)=H\xa3\x9c\x87\xb7B\x1a!l\x89\x03\u007f\x81\x82\x1d\xb2h\x00\x00\u07d4g\x9b\x9a\x10\x990Q~\x89\x99\t\x9c\xcf*\x91LL\x8d\xd94\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4g\xa8\x0e\x01\x90r\x1f\x949\rh\x02r\x9d\xd1,1\xa8\x95\xad\x89lk\x13u\xbc\x91V\x00\x00\u07d4g\xb8\xa6\xe9\x0f\xdf\n\x1c\xacD\x17\x930\x1e\x87P\xa9\xfayW\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4g\xbc\x85\xe8}\xc3LN\x80\xaa\xfa\x06k\xa8\u049d\xbb\x8eC\x8e\x89\x15\xd1\xcfAv\xae\xba\x00\x00\u07d4g\xc9&\t>\x9b\x89'\x938\x10\u0642\"\xd6.+\x82\x06\xbb\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4g\xcf\xdanp\xbfvW\u04d0Y\xb5\x97\x90\xe5\x14Z\xfd\xbea\x89#\x05\r\tXfX\x00\x00\u07d4g\u0582\xa2\x82\xefs\xfb\x8dn\x90q\xe2aOG\xab\x1d\x0f^\x8965\u026d\xc5\u07a0\x00\x00\u07d4g\u05a8\xaa\x1b\xf8\xd6\xea\xf78N\x99=\xfd\xf1\x0f\n\xf6\x8aa\x89\n\xbc\xbbW\x18\x97K\x80\x00\u07d4g\u0692.\xff\xa4r\xa6\xb1$\xe8N\xa8\xf8k$\xe0\xf5\x15\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4g\xdf$-$\r\u0538\a\x1dr\xf8\xfc\xf3[\xb3\x80\x9dq\xe8\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4g\xee@n\xa4\xa7\xaej:8\x1e\xb4\xed\xd2\xf0\x9f\x17KI(\x898)c_\th\xb0\x00\x00\u07d4g\xf2\xbbx\xb8\xd3\xe1\x1f|E\x8a\x10\xb5\xc8\xe0\xa1\xd3tF}\x89a\t=|,m8\x00\x00\u07d4g\xfcR}\xce\x17\x85\xf0\xfb\x8b\xc7\xe5\x18\xb1\xc6i\xf7\xec\u07f5\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4h\x02}\x19U\x8e\xd73\x9a\b\xae\xe8\xde5Y\xbe\x06>\xc2\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\x06@\x83\x8b\xd0zD{\x16\x8dm\x92;\x90\xcflC\xcd\u0289]\u0212\xaa\x111\xc8\x00\x00\u07d4h\a\xdd\u020d\xb4\x89\xb03\xe6\xb2\xf9\xa8\x15SW\x1a\xb3\xc8\x05\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94h\rY\x11\xed\x8d\xd9\xee\xc4\\\x06\f\"?\x89\xa7\xf6 \xbb\u054a\x04<3\xc1\x93ud\x80\x00\x00\u07d4h\x11\xb5L\u0456c\xb1\x1b\x94\xda\x1d\xe2D\x82\x85\u035fh\u0649;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4h\x19\f\xa8\x85\xdaB1\x87L\x1c\xfbB\xb1X\n!s\u007f8\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94h(\x97\xbcO\x8e\x89\x02\x91 \xfc\xff\xb7\x87\xc0\x1a\x93\xe6A\x84\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h)^\x8e\xa5\xaf\xd9\t?\xc0\xa4e\xd1W\x92+]*\xe24\x89\x01\x15NS!}\xdb\x00\x00\u07d4h.\x96'oQ\x8d1\xd7\xe5n0\u07f0\t\xc1!\x82\x01\xbd\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4h5\xc8\xe8\xb7J,\xa2\xae?J\x8d\x0fk\x95J>*\x83\x92\x89\x03B\x9c3]W\xfe\x00\x00\u07d4h63\x01\n\x88hk\xeaZ\x98\xeaS\xe8y\x97\xcb\xf7>i\x89\x05k9Bc\xa4\f\x00\x00\u07d4h=\xba6\xf7\xe9O@\xeaj\xea\ry\xb8\xf5!\xdeU\an\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4hA\x9cm\xd2\xd3\xceo\u02f3\xc7>/\xa0y\xf0`Q\xbd\xe6\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4hG;z}\x96Y\x04\xbe\u06e5V\u07fc\x17\x13l\xd5\xd44\x89\x05k\xc7^-c\x10\x00\x00\u07d4hG\x82[\xde\xe8$\x0e(\x04,\x83\xca\xd6B\U000868fd\u0709QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94hJD\xc0i3\x9d\b\xe1\x9auf\x8b\u06e3\x03\xbe\x85S2\x8a\x0e\u04b5%\x84\x1a\xdf\xc0\x00\x00\u07d4hS\x1fM\u0680\x8fS vz\x03\x114(\xca\f\xe2\xf3\x89\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4hy'\xe3\x04\x8b\xb5\x16*\xe7\xc1\\\xf7k\xd1$\xf9I{\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94h\x80\x9a\xf5\xd52\xa1\x1c\x1aMn2\xaa\xc7\\LR\xb0\x8e\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4h\x86\xad\xa7\xbb\xb0a{\u0684!\x91\u018c\x92.\xa3\xa8\xac\x82\x89>\xe2;\xde\x0e} \x00\x00\xe0\x94h\x88>\x15.V`\xfe\xe5\x96&\xe7\xe3\xb4\xf0Q\x10\xe6\"/\x8a\v\x94c;\xe9u\xa6*\x00\x00\u07d4h\x8aV\x9e\x96U$\xeb\x1d\n\xc3\xd3s>\xab\x90\x9f\xb3\xd6\x1e\x89G\x8e\xae\x0eW\x1b\xa0\x00\x00\xe0\x94h\x8e\xb3\x85;\xbc\xc5\x0e\xcf\xee\x0f\xa8\u007f\n\xb6\x93\u02bd\xef\x02\x8a\x06\xb1\n\x18@\x06G\xc0\x00\x00\u07d4h\xa7B_\xe0\x9e\xb2\x8c\xf8n\xb1y>A\xb2\x11\xe5{\u058d\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4h\xa8l@#\x88\xfd\xdcY\x02\x8f\xecp!\u933f\x83\x0e\xac\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\xe0\x94h\xac\u06a9\xfb\x17\xd3\xc3\t\x91\x1aw\xb0_S\x91\xfa\x03N\xe9\x8a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4h\xad\xdf\x01\x9dk\x9c\xabp\xac\xb1?\v1\x17\x99\x9f\x06.\x12\x89\x02\xb5\x12\x12\xe6\xb7\u0200\x00\u07d4h\xb3\x186\xa3\n\x01j\xda\x15{c\x8a\xc1]\xa7?\x18\xcf\u0789\x01h\u048e?\x00(\x00\x00\xe0\x94h\xb6\x85G\x88\xa7\xc6Il\xdb\xf5\xf8K\x9e\xc5\xef9+x\xbb\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4h\xc0\x84\x90\u021b\xf0\u05b6\xf3 \xb1\xac\xa9\\\x83\x12\xc0\x06\b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4h\xc7\xd1q\x1b\x01\x1a3\xf1o\x1fU\xb5\xc9\x02\xcc\xe9p\xbd\u05c9\b=lz\xabc`\x00\x00\u07d4h\xc8y\x1d\xc3B\xc3sv\x9e\xa6\x1f\xb7\xb5\x10\xf2Q\xd3 \x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\u07d4|I[\ubbb8\u8273\xf9S\xd53\x87K\xf1\x06\x89\x1d\x99E\xab+\x03H\x00\x00\u07d4h\xe8\x02'@\xf4\xaf)\xebH\xdb2\xbc\xec\xdd\xfd\x14\x8d=\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4h\xecy\u057eqUql@\x94\x1cy\u05cd\x17\u079e\xf8\x03\x89\x1b#8w\xb5 \x8c\x00\x00\u07d4h\xee\xc1\u222c1\xb6\xea\xba~\x1f\xbdO\x04\xadW\x9ak]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4h\xf5%\x92\x1d\xc1\x1c2\x9buO\xbf>R\x9f\xc7#\xc84\u0349WG=\x05\u06ba\xe8\x00\x00\u07d4h\xf7\x19\xae4+\xd7\xfe\xf1\x8a\x05\u02f0/pZ\u04ce\u0572\x898\xeb\xad\\\u0710(\x00\x00\xe0\x94h\xf7W<\xd4W\xe1L\x03\xfe\xa4>0-04|\x10p\\\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4h\xf8\xf4QU\xe9\x8cP)\xa4\xeb\u0175'\xa9.\x9f\xa81 \x89\xf0{D\xb4\a\x93 \x80\x00\u07d4h\xfe\x13W!\x8d\tXI\xcdW\x98B\u012a\x02\xff\x88\x8d\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x02(\xe4\xbb\x12\xa8\u0535\u09d7\xb0\xc5\xcf*u\t\x13\x1e\x89e\xea=\xb7UF`\x00\x00\u07d4i\x05\x94\xd3\x06a<\xd3\xe2\xfd$\xbc\xa9\x99J\u064a=s\xf8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94i\a2ir\x9ed\x14\xb2n\xc8\xdc\x0f\xd95\xc7;W\x9f\x1e\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94i\x19\xdd^]\xfb\x1a\xfa@G\x03\xb9\xfa\xea\x8c\xee5\xd0\rp\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4i4\x92\xa5\xc5\x13\x96\xa4\x82\x88\x16i\xcc\xf6\xd8\xd7y\xf0\tQ\x89\x12\xbfPP:\xe3\x03\x80\x00\u07d4i=\x83\xbe\tE\x9e\xf89\v.0\xd7\xf7\u008d\xe4\xb4(N\x89lk\x93[\x8b\xbd@\x00\x00\u07d4iQp\x83\xe3\x03\xd4\xfb\xb6\xc2\x11E\x14!]i\xbcF\xa2\x99\x89\x05k\xc7^-c\x10\x00\x00\u07d4iUPel\xbf\x90\xb7]\x92\xad\x91\"\xd9\r#\xcah\xcaM\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94iX\xf8;\xb2\xfd\xfb'\xce\x04\t\xcd\x03\xf9\xc5\xed\xbfL\xbe\u074a\x04<3\xc1\x93ud\x80\x00\x00\u0794i[\x0fRBu7\x01\xb2d\xa6pq\xa2\u0708\b6\xb8\u06c8\u3601\x1b\xech\x00\x00\xe0\x94i[L\xce\bXV\xd9\xe1\xf9\xff>y\x94 #5\x9e_\xbc\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94if\x06:\xa5\xde\x1d\xb5\xc6q\xf3\xddi\x9dZ\xbe!>\xe9\x02\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4it\u0224\x14\u03ae\xfd<.M\xfd\xbe\xf40V\x8d\x9a\x96\v\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\xe0\x94iximQP\xa9\xa2cQ?\x8ft\u0196\xf8\xb19|\xab\x8a\x01g\xf4\x82\xd3\u0171\xc0\x00\x00\xe0\x94iy{\xfb\x12\u027e\u0582\xb9\x1f\xbcY5\x91\xd5\xe4\x027(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94i\u007fUSk\xf8Z\xdaQ\x84\x1f\x02\x87b:\x9f\x0e\u041a\x17\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4i\x82\xfe\x8a\x86~\x93\xebJ\v\xd0QX\x93\x99\xf2\xec\x9aR\x92\x89lk\x93[\x8b\xbd@\x00\x00\u07d4i\x8a\x8ao\x01\xf9\xabh/c|yi\xbe\x88_lS\x02\xbf\x89\x01\r:\xa56\xe2\x94\x00\x00\u07d4i\x8a\xb9\xa2\xf33\x81\xe0|\fGC=\r!\xd6\xf36\xb1'\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4i\x94\xfb21\xd7\xe4\x1dI\x1a\x9dh\xd1\xfaL\xae,\xc1Y`\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\x9c\x9e\xe4q\x95Q\x1f5\xf8b\xcaL\"\xfd5\xae\x8f\xfb\xf4\x89\x04V9\x18$O@\x00\x00\u07d4i\x9f\xc6\u058aGuW<\x1d\u036e\xc80\xfe\xfdP9|N\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4i\xaf(\xb0tl\xac\r\xa1p\x84\xb99\x8c^6\xbb:\r\xf2\x896w\x03n\xdf\n\xf6\x00\x00\u07d4i\xb8\x0e\xd9\x0f\x84\x83J\xfa?\xf8.\xb9dp;V\tw\u0589\x01s\x17\x90SM\xf2\x00\x00\xe0\x94i\xb8\x1dY\x81\x14\x1e\u01e7\x14\x10`\xdf\u03cf5\x99\xff\xc6>\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4i\xbc\xfc\x1dC\xb4\xba\x19\xde{'K\xdf\xfb5\x13\x94\x12\xd3\u05c95e\x9e\xf9?\x0f\xc4\x00\x00\u07d4i\xbd%\xad\xe1\xa34lY\xc4\xe90\xdb*\x9dq^\xf0\xa2z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\xc0\x8dtGT\xdep\x9c\xe9n\x15\xae\r\x1d9[:\"c\x8965\u026d\xc5\u07a0\x00\x00\u07d4i\xc2\xd85\xf1>\xe9\x05\x80@\x8ej2\x83\xc8\u0326\xa44\xa2\x89#\x8f\xd4,\\\xf0@\x00\x00\u07d4i\xc9N\a\u0129\xbe3\x84\xd9]\xfa<\xb9)\x00Q\x87;{\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4i\xcb>!S\x99\x8d\x86\xe5\xee \xc1\xfc\u0466\xba\uec86?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4i\u04ddQ\b\x89\xe5R\xa3\x96\x13[\xfc\xdb\x06\xe3~8v3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94i\u064f8\xa3\xba=\xbc\x01\xfa\\,\x14'\xd8b\x83//p\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94i\xe2\xe2\xe7\x040|\xcc[\\\xa3\xf1d\xfe\xce.\xa7\xb2\xe5\x12\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4i\xffB\x90t\u02dblc\xbc\x91B\x84\xbc\xe5\xf0\xc8\xfb\xf7\u0409\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4i\xff\x89\x01\xb5Av?\x81|_)\x98\xf0-\xcf\xc1\xdf)\x97\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4j\x02:\xf5}XM\x84^i\x876\xf10\u06dd\xb4\r\xfa\x9a\x89\x05[ \x1c\x89\x00\x98\x00\x00\u07d4j\x04\xf5\xd5?\xc0\xf5\x15\xbe\x94+\x8f\x12\xa9\xcbz\xb0\xf3\x97x\x89\xa9\xaa\xb3E\x9b\xe1\x94\x00\x00\u07d4j\x05\xb2\x1cO\x17\xf9\xd7?_\xb2\xb0\u02c9\xffSV\xa6\xcc~\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\xe0\x94j\x0f\x05`f\xc2\xd5f(\x85\x02s\xd7\xec\xb7\xf8\xe6\xe9\x12\x9e\x8a\x01\x0f\r)<\u01e5\x88\x00\x00\u07d4j\x13\xd5\xe3,\x1f\xd2m~\x91\xffn\x051`\xa8\x9b,\x8a\xad\x89\x02\xe6/ \xa6\x9b\xe4\x00\x00\u07d4j.\x86F\x9a[\xf3|\xee\x82\xe8\x8bL8c\x89](\xfc\xaf\x89\x1c\"\x92f8[\xbc\x00\x00\u07d4j6\x94BL|\u01b8\xbc\u067c\u02baT\f\xc1\xf5\xdf\x18\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94jB\u0297\x1cex\u056d\xe2\x95\xc3\xe7\xf4\xad3\x1d\xd3BN\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4jD\xaf\x96\xb3\xf02\xaed\x1b\xebg\xf4\xb6\xc83B\xd3|]\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4jL\x89\a\xb6\x00$\x80W\xb1\xe4cT\xb1\x9b\u0705\x9c\x99\x1a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jQNbB\xf6\xb6\x8c\x13~\x97\xfe\xa1\u73b5U\xa7\xe5\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4jS\xd4\x1a\xe4\xa7R\xb2\x1a\xbe\xd57FI\x95:Q=\xe5\xe5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4jaY\aJ\xb5s\xe0\xeeX\x1f\x0f=\xf2\u05a5\x94b\x9bt\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4jc7\x83?\x8fjk\xf1\f\xa7\xec!\xaa\x81\x0e\xd4D\xf4\u02c97\xbd$4\\\xe8\xa4\x00\x00\u07d4jcS\xb9qX\x9f\x18\xf2\x95\\\xba(\xab\xe8\xac\xcejWa\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4jc\xfc\x89\xab\xc7\xf3n(-\x80x{{\x04\xaf\xd6U>q\x89\b\xacr0H\x9e\x80\x00\x00\u07d4jg\x9e7\x8f\xdc\xe6\xbf\xd9\u007f\xe6/\x04<od\x05\u05de\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4jhk\xf2 \xb5\x93\u07b9\xb72F\x15\xfb\x91D\xde\xd3\xf3\x9d\x89O%\x91\xf8\x96\xa6P\x00\x00\xe0\x94jk\x18\xa4ZvF~.]Z.\xf9\x11\xc3\xe1))\x85{\x8a\x11]:\x99\xa9aO@\x00\x00\u07d4jt\x84M\x8e\x9c\xb5X\x1cE\a\x9a.\x94F*l\xee\x88!\x89:\xb5:U-\xd4\xc9\x00\x00\xe0\x94j{.\r\x88\x86\u007f\xf1] |\"+\xeb\xf9O\xa6\u0383\x97\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4j|% B\xe7F\x8a?\xf7s\xd6E\v\xba\x85\xef\xa2c\x91\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4j\x8aC\x17\xc4_\xaa\x05T\xcc\xdbH%H\x18>)Z$\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4j\x8c\xea-\xe8J\x8d\xf9\x97\xfd?\x84\xe3\b=\x93\xdeW\u0369\x89\x05k\xe0<\xa3\xe4}\x80\x00\xe0\x94j\x97Xt;`>\xea:\xa0RKB\x88\x97#\xc4\x159H\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4j\xa5s/;\x86\xfb\x8c\x81\xef\xbek[G\xb5cs\v\x06\u020965\u026d\xc5\u07a0\x00\x00\u07d4j\xb3#\xaePV\xed\nE0r\u016b\xe2\xe4/\xcf]q9\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4j\xb5\xb4\xc4\x1c\u0778)i\f/\xda\u007f \xc8^b\x9d\xd5\u0549d\u052fqL2\x90\x00\x00\u07d4j\xc4\x0fS-\xfe\xe5\x11\x81\x17\u04ad5-\xa7}Om\xa2\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xc4\u053e-\xb0\u065d\xa3\xfa\xaa\xf7RZ\xf2\x82\x05\x1dj\x90\x89\x04X\xcaX\xa9b\xb2\x80\x00\u07d4j\xcd\u0723\xcd+I\x90\xe2\\\xd6\\$\x14\x9d\t\x12\t\x9ey\x89\xa2\xa1\xe0|\x9fl\x90\x80\x00\u07d4j\xd9\v\xe2R\xd9\xcdFM\x99\x81%\xfa\xb6\x93\x06\v\xa8\xe4)\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4j\u0753!\x93\xcd8IJ\xa3\xf0:\xec\xccKz\xb7\xfa\xbc\xa2\x89\x04\xdbs%Gc\x00\x00\x00\xe0\x94j\xe5\u007f'\x91|V*\x13*M\x1b\xf7\xec\n\u01c5\x83)&\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4j\xeb\x9ftt.\xa4\x91\x81=\xbb\xf0\xd6\xfc\xde\x1a\x13\x1dM\xb3\x89\x17\xe5T0\x8a\xa00\x00\x00\u07d4j\xf25\u04bb\xe0P\xe6)\x16\x15\xb7\x1c\xa5\x82\x96X\x81\x01B\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf6\xc7\xee\x99\xdf'\x1b\xa1[\xf3\x84\xc0\xb7d\xad\xcbM\xa1\x82\x8965f3\xeb\xd8\xea\x00\x00\u07d4j\xf8\xe5Yih,q_H\xadO\xc0\xfb\xb6~\xb5\x97\x95\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4j\xf9@\xf6>\u0278\xd8v'*\u0296\xfe\xf6\\\xda\xce\xcd\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4j\xf9\xf0\xdf\uebbb_d\xbf\x91\xabw\x16i\xbf\x05)US\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4j\xff\x14f\xc2b6u\xe3\xcb\x0eu\xe4#\xd3z%\xe4B\xeb\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94k\r\xa2Z\xf2g\u05c3l\"k\xca\xe8\xd8r\xd2\xceR\xc9A\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4k\x10\xf8\xf8\xb3\xe3\xb6\r\xe9\n\xa1-\x15_\x9f\xf5\xff\xb2,P\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x17Y\x8a\x8e\xf5Oyz\xe5\x15\u0336Q}\x18Y\xbf\x80\x11\x89\x05k\xc7^-c\x10\x00\x00\u07d4k \xc0\x80`jy\xc7;\xd8\xe7[\x11qzN\x8d\xb3\xf1\u00c9\x10?sX\x03\xf0\x14\x00\x00\u07d4k\"\x84D\x02!\xce\x16\xa88-\xe5\xff\x02)G\"i\xde\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4k0\xf1\x829\x10\xb8m:\xcbZj\xfc\x9d\xef\xb6\xf3\xa3\v\xf8\x89\u3bb5sr@\xa0\x00\x00\u07d4k8\u0784\x1f\xad\u007fS\xfe\x02\xda\x11[\xd8j\xaff$f\xbd\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4kK\x99\xcb?\xa9\xf7\xb7L\u3903\x17\xb1\xcd\x13\t\n\x1az\x89\x03\x1b2~i]\xe2\x00\x00\u07d4kZ\xe7\xbfx\xecu\xe9\f\xb5\x03\xc7x\xcc\u04f2KO\x1a\xaf\x89+^:\xf1k\x18\x80\x00\x00\u07d4kc\xa2\u07f2\xbc\xd0\xca\xec\x00\"\xb8\x8b\xe3\f\x14Q\xeaV\xaa\x89+\xdbk\xf9\x1f\u007fL\x80\x00\u07d4kew\xf3\x90\x9aMm\xe0\xf4\x11R-Ep8d\x004\\\x89e\xea=\xb7UF`\x00\x00\u07d4kr\xa8\xf0a\xcf\xe6\x99j\xd4G\xd3\xc7,(\xc0\xc0\x8a\xb3\xa7\x89\xe7\x8cj\u01d9\x12b\x00\x00\u07d4kv\rHw\xe6\xa6'\xc1\xc9g\xbe\xe4Q\xa8P}\xdd\u06eb\x891T\xc9r\x9d\x05x\x00\x00\u07d4k\x83\xba\xe7\xb5e$EXU[\xcfK\xa8\xda \x11\x89\x1c\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\x92]\xd5\xd8\xeda2\xabm\b`\xb8,D\xe1\xa5\x1f\x1f\xee\x89P; >\x9f\xba \x00\x00\xe0\x94k\x94a]\xb7Pej\u00cc~\x1c\xf2\x9a\x9d\x13g\u007fN\x15\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4k\x95\x1aC'N\xea\xfc\x8a\t\x03\xb0\xaf.\xc9+\xf1\xef\xc89\x89\x05k\xc7^-c\x10\x00\x00\u07d4k\x99%!\xec\x85#p\x84\x8a\u0597\xcc-\xf6Nc\xcc\x06\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xa8\xf7\xe2_\xc2\xd8qa\x8e$\xe4\x01\x84\x19\x917\xf9\xf6\xaa\x89\x15\xafd\x86\x9ak\xc2\x00\x00\u07d4k\xa9\xb2\x1b5\x10k\xe1Y\xd1\xc1\xc2ez\xc5l\u049f\xfdD\x89\xf2\xdc}G\xf1V\x00\x00\x00\u07d4k\xafz*\x02\xaex\x80\x1e\x89\x04\xadz\xc0Q\b\xfcV\xcf\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94k\xb2\xac\xa2?\xa1bm\x18\xef\xd6w\u007f\xb9}\xb0-\x8e\n\xe4\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4k\xb4\xa6a\xa3:q\xd4$\u051b\xb5\xdf(b.\xd4\xdf\xfc\xf4\x89\",\x8e\xb3\xfff@\x00\x00\u07d4k\xb5\b\x13\x14j\x9a\xddB\xee\"\x03\x8c\x9f\x1fti\xd4\u007fG\x89\n\xdaUGK\x814\x00\x00\u07d4k\xbc?5\x8af\x8d\u0461\x1f\x03\x80\xf3\xf71\bBj\xbdJ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4k\xbd\x1eq\x93\x90\xe6\xb9\x10C\xf8\xb6\xb9\u07c9\x8e\xa8\x00\x1b4\x89llO\xa6\xc3\xdaX\x80\x00\u07d4k\xc8Z\xcdY(r.\xf5\tS1\xee\x88\xf4\x84\xb8\u03c3W\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4k\xd3\xe5\x9f#\x9f\xaf\xe4wk\xb9\xbd\xddk\ue0fa]\x9d\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4k\xd4W\xad\xe0Qy]\xf3\xf2F\\89\xae\xd3\xc5\xde\xe9x\x8964\xbf9\xab\x98x\x80\x00\u07d4k\xe1c\x13d>\xbc\x91\xff\x9b\xb1\xa2\xe1\x16\xb8T\xea\x93:E\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4k\xe7Y^\xa0\xf0hH\x9a'\x01\xecFI\x15\x8d\xdcC\xe1x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4k\xe9\x03\x0e\xe6\xe2\xfb\u0111\xac\xa3\xde@\"\xd3\x01w+{}\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94k\xec1\x1a\xd0P\b\xb4\xaf5<\x95\x8c@\xbd\x06s\x9a?\xf3\x8a\x03w\xf6*\x0f\nbp\x00\x00\u07d4k\xf7\xb3\xc0e\xf2\xc1\xe7\xc6\xeb\t+\xa0\xd1Pf\xf3\x93\u0478\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4k\xf8o\x1e/+\x802\xa9\\Mw8\xa1\t\xd3\xd0\xed\x81\x04\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4l\x05\xe3N^\xf2\xf4.\u041d\xef\xf1\x02l\xd6k\xcbi`\xbb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4l\b\xa6\xdc\x01s\xc74)U\xd1\xd3\xf2\xc0e\xd6/\x83\xae\u01c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\n\xe9\xf0C\xc84\xd4Bq\xf14\x06Y=\xfe\tO8\x9f\x89RD*\xe13\xb6*\x80\x00\u07d4l\f\xc9\x17\xcb\xee}|\t\x97c\xf1Nd\xdf}4\xe2\xbf\t\x89\r\x8drkqw\xa8\x00\x00\xe0\x94l\x0eq/@\\Yr_\xe8)\xe9wK\xf4\xdf\u007fM\xd9e\x8a\f(h\x88\x9c\xa6\x8aD\x00\x00\xe0\x94l\x10\x12\x05\xb3#\xd7uD\xd6\xdcR\xaf7\xac\xa3\xce\xc6\xf7\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4l\x15\xec5 \xbf\x8e\xbb\xc8 \xbd\x0f\xf1\x97x7T\x94\u03dd\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\xe0\x94l\x1d\xdd3\xc8\x19f\u0706!w`q\xa4\x12\x94\x82\xf2\xc6_\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4l%2\u007f\x8d\u02f2\xf4^V\x1e\x86\xe3]\x88P\xe5:\xb0Y\x89;\xcd\xf9\xba\xfe\xf2\xf0\x00\x00\u07d4l.\x9b\xe6\u052bE\x0f\xd1%1\xf3?\x02\x8caFt\xf1\x97\x89\xc2\x12z\xf8X\xdap\x00\x00\u07d4l5\x9eX\xa1=Ex\xa93\x8e3\\g\xe7c\x9f_\xb4\u05c9\v\xd1[\x94\xfc\x8b(\x00\x00\u07d4l=\x18pA&\xaa\x99\xee3B\xce`\xf5\xd4\xc8_\x18g\u0349\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4lGK\xc6jTx\x00f\xaaOQ.\xef\xa7s\xab\xf9\x19\u01c9\x05\x18\x83\x15\xf7v\xb8\x00\x00\u07d4lNBn\x8d\xc0\x05\u07e3Ql\xb8\xa6\x80\xb0.\ua56e\x8e\x89Hz\x9a0E9D\x00\x00\u07d4lR\xcf\b\x95\xbb5\xe6V\x16\x1eM\xc4j\xe0\xe9m\xd3\xe6,\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4lT\"\xfbK\x14\xe6\u064b`\x91\xfd\xecq\xf1\xf0\x86@A\x9d\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4l\\:T\u0367\xc2\xf1\x18\xed\xbaCN\xd8\x1en\xbb\x11\xddz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4lc\xf8EV\u0490\xbf\u0359\xe44\ue657\xbf\xd7yWz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4lc\xfc\x85\x02\x9a&T\u05db+\xeaM\xe3I\xe4REw\u0149#\xc7W\a+\x8d\xd0\x00\x00\u07d4led\xe5\xc9\xc2N\xaa\xa7D\xc9\xc7\xc9h\xc9\xe2\xc9\xf1\xfb\xae\x89I\x9bB\xa2\x119d\x00\x00\xe0\x94lg\xd6\xdb\x1d\x03Ql\x12\x8b\x8f\xf24\xbf=I\xb2m)A\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4lg\xe0\u05f6.*\bPiE\xa5\xdf\xe3\x82c3\x9f\x1f\"\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4lj\xa0\xd3\vdr\x19\x90\xb9PJ\x86?\xa0\xbf\xb5\xe5}\xa7\x89\x92^\x06\xee\xc9r\xb0\x00\x00\u07d4lqJX\xff\xf6\xe9}\x14\xb8\xa5\xe3\x05\xeb$@eh\x8b\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4l\x80\rKI\xba\a%\x04`\xf9\x93\xb8\xcb\xe0\v&j%S\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4l\x80\x8c\xab\xb8\xff_\xbbc\x12\xd9\xc8\xe8J\xf8\xcf\x12\xef\bu\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94l\x82 )!\x8a\xc8\xe9\x8a&\f\x1e\x06@)4\x889\x87[\x8a\x01\x0f\x97\xb7\x87\xe1\xe3\b\x00\x00\u07d4l\x84\u02e7|m\xb4\xf7\xf9\x0e\xf1=^\xe2\x1e\x8c\xfc\u007f\x83\x14\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\x86\x87\xe3Aw\x10\xbb\x8a\x93U\x90!\xa1F\x9ej\x86\xbcw\x8a\x02[-\xa2x\xd9k{\x80\x00\xe0\x94l\x88,'s,\xef\\|\x13\xa6\x86\xf0\xa2\xeawUZ\u0089\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4l\xa5\xde\x00\x81}\xe0\xce\xdc\xe5\xfd\x00\x01(\xde\xde\x12d\x8b<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4l\xa6\xa12\xce\x1c\u0488\xbe\xe3\x0e\xc7\xcf\xef\xfb\x85\xc1\xf5\nT\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94l\xb1\x1e\xcb2\xd3\u0382\x96\x011\x066\xf5\xa1\f\xf7\u03db_\x8a\x04?\u851c8\x01\xf5\x00\x00\u07d4l\xc1\xc8x\xfal\u078a\x9a\v\x83\x11$~t\x1eFB\xfem\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94l\xcb\x03\xac\xf7\xf5<\xe8z\xad\xcc!\xa9\x93-\xe9\x15\xf8\x98\x04\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4l\xd2\x12\xae\xe0N\x01?=*\xba\u04a0#`k\xfb\\j\u01c9lj\xccg\u05f1\xd4\x00\x00\u07d4l\xd2(\xdcq!i0\u007f\xe2|\xebtw\xb4\x8c\xfc\x82r\xe5\x89\x044\xea\x94\u06caP\x00\x00\u07d4l\xe1\xb0\xf6\xad\xc4pQ\xe8\xab8\xb3\x9e\xdbA\x86\xb0;\xab\u0309Ay\x97\x94\xcd$\xcc\x00\x00\u07d4l\xea\xe3s=\x8f\xa4=l\xd8\f\x1a\x96\xe8\xeb\x93\x10\x9c\x83\xb7\x89\x10'\x94\xad \xdah\x00\x00\u07d4m\x05i\xe5U\x8f\xc7\xdf'f\xf2\xba\x15\u070a\xef\xfc[\xebu\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4m\x12\x0f\f\xaa\xe4O\xd9K\xca\xfeU\xe2\xe2y\uf5ba\\z\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\x14V\xff\xf0\x10N\xe8D\xa31G7\x8438\xd2L\xd6l\x89\a\xb0l\xe8\u007f\xddh\x00\x00\u07d4m \xef\x97\x04g\nP\v\xb2i\xb5\x83.\x85\x98\x02\x04\x9f\x01\x89\a\f\x1c\xc7;\x00\xc8\x00\x00\xe0\x94m/\x97g4\xb9\xd0\a\r\x18\x83\xcfz\u02b8\xb3\xe4\x92\x0f\xc1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m9\xa9\u93c1\xf7i\xd7:\xad,\xea\xd2v\xac\x13\x87\xba\xbe\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4m;x6\xa2\xb9\u0619r\x1aM#{R#\x85\xdc\xe8\xdf\u034966\xc2^f\xec\xe7\x00\x00\u07d4m?+\xa8V\u033b\x027\xfava\x15k\x14\xb0\x13\xf2\x12@\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94m@\b\xb4\xa8\x88\xa8&\xf2H\xeej\v\r\xfd\xe9\xf92\x10\xb9\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4m@\xca'\x82m\x97s\x1b>\x86\xef\xfc\u05f9*Aa\xfe\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mD\x97J1\u0447\xed\xa1m\xddG\xb9\xc7\xecP\x02\xd6\x1f\xbe\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94mK\\\x05\xd0j \x95~\x17H\xabm\xf2\x06\xf3C\xf9/\x01\x8a\x02\x1f6\x06\x99\xbf\x82_\x80\x00\xe0\x94mL\xbf=\x82\x84\x83:\xe9\x93D0>\b\xb4\xd6\x14\xbf\xda;\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4mY\xb2\x1c\xd0\xe2t\x88\x04\u066b\xe0d\xea\u00be\xf0\xc9_'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4mc\u04ce\xe8\xb9\x0e\x0en\xd8\xf1\x92\xed\xa0Q\xb2\u05a5\x8b\xfd\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4mf4\xb5\xb8\xa4\x01\x95\xd9I\x02z\xf4\x82\x88\x02\t,\ued89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94m}\x1c\x94\x95\x11\xf8\x83\x03\x80\x8c`\xc5\xea\x06@\xfc\xc0&\x83\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4m\x84m\xc1&W\xe9\x1a\xf2P\bQ\x9c>\x85\u007fQp}\u0589\xf8\xd3\v\xc9#B\xf8\x00\x00\u07d4m\x91\x93\x99k\x19F\x17!\x11\x06\xd1c^\xb2l\u0136ll\x89\x15\xaa\x1e~\x9d\xd5\x1c\x00\x00\u07d4m\x99\x97P\x98\x82\x02~\xa9G#\x14$\xbe\xde\xde)e\u043a\x89l\x81\u01f3\x11\x95\xe0\x00\x00\u07d4m\xa0\xed\x8f\x1di3\x9f\x05\x9f*\x0e\x02G\x1c\xb4O\xb8\u00fb\x892\xbc8\xbbc\xa8\x16\x00\x00\u07d4m\xb7+\xfdC\xfe\xf4e\xcaV2\xb4Z\xabra@N\x13\xbf\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xbe\x8a\xbf\xa1t(\x06&9\x817\x1b\xf3\xd3U\x90\x80kn\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xc3\xf9+\xaa\x1d!\u06b78+\x892a\xa05o\xa7\xc1\x87\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4m\xc7\x05:q\x86\x16\xcf\u01cb\xeec\x82\xeeQ\xad\xd0\xc7\x030\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94m\xcc~d\xfc\xaf\xcb\xc2\xdcl\x0e^f,\xb3G\xbf\xfc\xd7\x02\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4m\xda_x\x8alh\x8d\u07d2\x1f\xa3\x85.\xb6\xd6\xc6\xc6)f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4m\xdb`\x92w\x9dXB\xea\xd3x\xe2\x1e\x81 \xfdLk\xc12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4m\xdf\xefc\x91U\u06ab\n\\\xb4\x95:\xa8\u016f\xaa\x88\x04S\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4m\xe0/-\xd6~\xfd\xb794\x02\xfa\x9e\xaa\xcb\xcfX\x9d.V\x89@\x13\x8b\x91~\u07f8\x00\x00\u07d4m\u4d418\\\xf7\xfc\x9f\xe8\xc7}\x13\x1f\xe2\xeew$\xc7j\x89})\x97s=\xcc\xe4\x00\x00\u07d4m\xe4\xd1R\x19\x18/\xaf:\xa2\xc5\xd4\xd2Y_\xf20\x91\xa7'\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4m\xed\xf6.t?M,*K\x87\xa7\x87\xf5BJz\xeb9<\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4m\xf2Of\x85\xa6/y\x1b\xa37\xbf?\xf6~\x91\xf3\u053c:\x89ukI\xd4\nH\x18\x00\x00\u07d4m\xf5\xc8O{\x90\x9a\xab>a\xfe\x0e\xcb\x1b;\xf2`\"*\u0489\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4m\xff\x90\xe6\xdc5\x9d%\x90\x88+\x14\x83\xed\xbc\xf8\x87\xc0\xe4#\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\x01\xe4\xadV\x9c\x95\xd0\a\xad\xa3\r^-\xb1(\x88I\"\x94\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\a;f\u0478\xc6gD\u0600\x96\xa8\u0759\xec~\x02(\u0689\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x0e\xe7\x06\x12\xc9v(}I\x9d\u07e6\xc0\xdc\xc1,\x06\xde\xea\x89\a\v\u0579V!F\x00\x00\xe0\x94n\x12\xb5\x1e\"[JCr\xe5\x9a\u05e2\xa1\xa1>\xa3\u04e17\x8a\x03\x00F\xc8\xccw_\x04\x00\x00\u07d4n\x1a\x04l\xaf[JW\xf4\xfdK\xc1sb!&\xb4\xe2\xfd\x86\x89a\t=|,m8\x00\x00\u07d4n\x1e\xa4\xb1\x83\xe2R\u027bwg\xa0\x06\u05346\x96\u02ca\xe9\x89\x0f\xf3x<\x85\xee\u0400\x00\u07d4n%[p\n\xe7\x13\x8aK\xac\xf2(\x88\xa9\xe2\xc0\n(^\xec\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n'\n\xd5)\xf1\xf0\xb8\xd9\xcbm$'\xec\x1b~-\xc6Jt\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4n.\xab\x85\u0709\xfe)\xdc\n\xa1\x852G\u06b4:R=V\x89\x04V9\x18$O@\x00\x00\u07d4n:Q\xdbt=3M/\xe8\x82$\xb5\xfe|\x00\x8e\x80\xe6$\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4nL*\xb7\xdb\x02i9\xdb\u04fch8J\xf6`\xa6\x18\x16\xb2\x89\t\r\x97/22<\x00\x00\u07d4nM.9\u0203f)\u5d07\xb1\x91\x8af\x9a\xeb\u07556\x8965\u026d\xc5\u07a0\x00\x00\u07d4n\\-\x9b\x1cTj\x86\xee\xfd]\nQ \xc9\xe4\xe70\x19\x0e\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4n`\xae\u19cf\x8e\u068bBLs\xe3S5J\xe6|0B\x89\xbd5\xa4\x8d\x99\x19\xe6\x00\x00\u07d4nd\xe6\x12\x9f\"N7\x8c\x0ensj~z\x06\xc2\x11\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4nm[\xbb\xb9\x05;\x89\xd7D\xa2s\x16\u00a7\xb8\xc0\x9bT}\x891Rq\n\x02>m\x80\x00\u07d4nr\xb2\xa1\x18j\x8e)\x16T;\x1c\xb3jh\x87\x0e\xa5\u0457\x89\n\x15D\xbe\x87\x9e\xa8\x00\x00\u07d4nv\x1e\xaa\x0f4_w{TA\xb7:\x0f\xa5\xb5k\x85\xf2-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4ny\xed\u0504[\anL\u060d\x18\x8bnC-\xd9?5\xaa\x893\xc5I\x901r\f\x00\x00\u07d4n\x82\x12\xb7\"\xaf\xd4\b\xa7\xa7>\xd3\xe29^\xe6EJ\x030\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\u07d4n\x84\x87m\xbb\x95\xc4\vfV\xe4+\xa9\xae\xa0\x8a\x99;T\u0709;\xbc`\xe3\xb6\u02fe\x00\x00\u07d4n\x84\xc2\xfd\x18\xd8\tW\x14\xa9h\x17\x18\x9c\xa2\x1c\xcab\xba\xb1\x89\x12{lp&!\u0340\x00\u07d4n\x86m\x03-@Z\xbd\xd6\\\xf6QA\x1d\x807\x96\xc2#\x11\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94n\x89\x9eY\xa9\xb4\x1a\xb7\xeaA\xdfu\x17\x86\x0f*\xcbY\xf4\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4n\x89\xc5\x1e\xa6\xde\x13\xe0l\xdct\x8bg\xc4A\x0f\u9f2b\x03\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\x8a&h\x9fz/\xde\xfd\x00\x9c\xba\xaaS\x10%4P\u06ba\x89o!7\x17\xba\xd8\xd3\x00\x00\u07d4n\x96\xfa\xed\xa3\x05C\x02\xc4_X\xf1a2L\x99\xa3\xee\xbbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xb0\xa5\xa9\xae\x96\xd2,\xf0\x1d\x8f\xd6H;\x9f8\xf0\x8c,\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xb3\x81\x96\x17@@X&\x8f\f<\xff5\x96\xbf\xe9\x14\x8c\x1c\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94n\xb5W\x8ak\xb7\xc3!S\x19[\r\x80 \xa6\x91HR\xc0Y\x8a\x8b\u00ab\xf4\x02!\xf4\x80\x00\x00\u07d4n\xbb^iW\xaa\x82\x1e\xf6Y\xb6\x01\x8a9:PL\xaeDP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\xbc\xf9\x95\u007f_\xc5\u916d\xd4u\";\x04\xb8\xc1Jz\xed\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4n\xc3e\x95q\xb1\x1f\x88\x9d\xd49\xbc\xd4\xd6u\x10\xa2[\xe5~\x89\x06\xaa\xf7\xc8Qm\f\x00\x00\u07d4n\u021b9\xf9\xf5'jU>\x8d\xa3\x0en\xc1z\xa4~\xef\u01c9\x18BO_\v\x1bN\x00\x00\u07d4n\xc9m\x13\xbd\xb2M\u01e5W)?\x02\x9e\x02\xddt\xb9zU\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xca\xef\xa6\xfc>\xe54bm\xb0,o\x85\xa0\u00d5W\x1ew\x89 \x86\xac5\x10R`\x00\x00\u07d4n\u04a1+\x02\xf8\u0188\u01f5\u04e6\xea\x14\xd66\x87\u06b3\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4n\u0604E\x9f\x80\x9d\xfa\x10\x16\xe7p\xed\xaf>\x9f\xefF\xfa0\x89\xb8R\xd6x \x93\xf1\x00\x00\xe0\x94n\xdf\u007fR\x83r\\\x95>\xe6C\x17\xf6a\x88\xaf\x11\x84\xb03\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4n\xe8\xaa\xd7\xe0\xa0e\u0605-|;\x9an_\xdcK\xf5\f\x00\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4n\xef\u0705\x0e\x87\xb7\x15\xc7'\x91w<\x03\x16\xc3U\x9bX\xa4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4n\xf9\xe8\u0276!}Vv\x9a\xf9}\xbb\x1c\x8e\x1b\x8b\xe7\x99\u0489\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4n\xfb\xa8\xfb*\u0176s\a)\xa9r\xec\"D&\xa2\x87\u00ed\x89\x0fY\x85\xfb\xcb\xe1h\x00\x00\xe0\x94n\xfd\x90\xb55\xe0\v\xbd\x88\x9f\xda~\x9c1\x84\xf8y\xa1Q\u06ca\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4o\x05\x16f\xcbO{\u04b1\x90r!\xb8)\xb5U\u05e3\xdbt\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4o\x0e\xdd#\xbc\xd8_`\x15\xf9(\x9c(\x84\x1f\xe0L\x83\xef\xeb\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4o\x13zq\xa6\xf1\x97\xdf,\xbb\xf0\x10\u073d<DN\xf5\xc9%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o\x17`e\xe8\x8e<o\xe6&&}\x18\xa0\x88\xaa\xa4\u06c0\xbc\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4o\x18\xecv~2\x05\b\x19_\x13tP\x0e?.\x12V\x89\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4o\x1fI\a\xb8\xf6\x1f\fQV\x8di(\x06\xb3\x82\xf5\x03$\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o$\u026f+v4\x80Q]\x1b\tQ\xbbw\xa5@\xf1\xe3\xf9\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4o%v\xdaM\u20fb\xe8\xe3\xeei\xdd\xd6n^q\x1d\xb3\xf5\x89DY\x1dg\xfe\u0300\x00\x00\u07d4o)\xbb7[\xe5\xed4\ud65b\xb80\xee)W\xdd\xe7m\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o*1\x90\x0e$\x03\x95\xb1\x9f\x15\x9c\x1d\x00\xdf\xe4\u0618\xeb\u07c9lf\x06E\xaaG\x18\x00\x00\u07d4o*B\xe6\xe03\xd0\x10a\x13\x19)\xf7\xa6\xee\x158\x02\x1eR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o9\xcc7\u02aa-\u071ba\x0fa1\xe0a\x9f\xaew*<\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4oD\xca\t\xf0\u01a8)L\xbdQ\x9c\xdcYJ\xd4,gW\x9f\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4oP\x92\x97w\x82L)\x1aI\xc4m\xc8T\xf3y\xa6\xbe\xa0\x80\x89\x13\x84\x00\xec\xa3d\xa0\x00\x00\xe0\x94ol\xf2\x06I\xa9\xe9s\x17z\xc6}\xba\xde\xe4\xeb\xe5\u01fd\u068a\x01\x13c)}\x01\xa8`\x00\x00\u07d4oy\x1d5\x9b\xc3Sj1]c\x82\xb8\x83\x11\xaf\x8e\xd6\xdaG\x89\x04\xfc\xc1\xa8\x90'\xf0\x00\x00\u07d4oyM\xbd\xf6#\u06a6\xe0\xd0\at\xadibs|\x92\x1e\xa4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4oz\u0181\xd4^A\x8f\u038b:\x1d\xb5\xbc;\xe6\xf0l\x98I\x89lk\x93[\x8b\xbd@\x00\x00\u07d4o\x81\xf3\xab\xb1\xf93\xb1\xdf9k\x8e\x9c\xc7#\xa8\x9b|\x98\x06\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4o\x8f\r\x15\u0316\xfb\u007f\xe9O\x10e\xbci@\xf8\xd1)W\xb2\x8965\u026d\xc5\u07a0\x00\x00\u07d4o\x92\xd6\xe4T\x8cx\x99e\t\xeehK.\u26e3\xc52\xb4\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94o\xa6\r\xf8\x18\xa5Dd\x18\xb1\xbb\xd6(&\u0e42^\x13\x18\x8a\x02\u02d2\u030fg\x14@\x00\x00\u07d4o\xa68\x8d@+0\xaf\xe5\x994\u00f9\xe1=\x11\x86G`\x18\x89$R\x1e*0\x17\xb8\x00\x00\u07d4o\xa7 \x15\xfaxin\xfd\x9a\x86\x17O\u007f\x1f!\x01\x92\x86\xb1\x89Hz\x9a0E9D\x00\x00\u07d4o\xc2^~\x00\xcaO`\xa9\xfeo(\xd1\xfd\xe3T.-\x10y\x89*\xef5;\xcd\xdd`\x00\x00\u07d4o\xc56b7\x1d\xcaX{Y\x85\r\xe7\x86\x06\xe25\x9d\xf3\x83\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94o\xcc,s+\u0753J\xf6\xcc\xd1hF\xfb&\uf272\xaa\x9b\x8a\x02\x1e+\x1dB&\x1dI\x00\x00\u07d4o\xd4\xe0\xf3\xf3+\xeem7g\xfd\xbc\x9d5:m:\xabx\x99\x89%\xb0d\xa8u\xea\x94\x00\x00\xe0\x94o\xd9G\u0567;\x17P\b\xaen\xe8\"\x81c\xda(\x9b\x16}\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4o\u064eV=\x12\xce\x0f\xd6\x0fO\x1f\x85\n\u35a9\x82<\x02\x89D[\xe3\xf2\uf1d4\x00\x00\xe0\x94o\u077d\x9b\xcaf\xe2\x87e\xc2\x16,\x843T\x8c\x10R\xed\x11\x8a\x11\x84B\x9b\x82\xa8\x18\x80\x00\x00\u07d4o\xf5\xd3a\xb5*\u0436\x8b\x15\x88`~\xc3\x04\xaeVe\xfc\x98\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4o\xf6\u0310\xd6I\xdeN\x96\xcf\xfe\xe1\az[0*\x84\x8d\u02c9\x01\x8c\xe7\x9cx\x80,\x00\x00\u07d4o\xfe\\\xf8,\xc9\xea^6\xca\xd7\u0097L\xe7$\x9f7I\xe6\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94p\x05\xa7r(+\x1fb\xaf\xdac\xf8\x9b]\u01abd\xc8L\xb9\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4p\a\x11\xe3\x11\xbb\x94sU\xf7U\xb5y%\f\xa7\xfdvZ>\x89a\t=|,m8\x00\x00\u07d4p\x10\xbe-\xf5{\u042b\x9a\xe8\x19l\xd5\n\xb0\xc5!\xab\xa9\xf9\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4p#\xc7\tV\xe0J\x92\xd7\x00%\xaa\u0497\xb59\xaf5Xi\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p%\x96]+\x88\xda\x19}DY\xbe=\xc98cD\xcc\x1f1\x89l\xb7\xe7Hg\xd5\xe6\x00\x00\u07d4p(\x02\xf3m\x00%\x0f\xabS\xad\xbc\u0596\xf0\x17oc\x8aI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pH\x19\xd2\xe4Mn\xd1\xda%\xbf\u0384\u011f\u0322V\x13\xe5\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4pJn\xb4\x1b\xa3O\x13\xad\xdd\xe7\xd2\xdb}\xf0I\x15\u01e2!\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4pJ\xb1\x15\r^\x10\xf5\xe3I\x95\b\xf0\xbfpe\x0f\x02\x8dK\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4pJ\xe2\x1dv-n\x1d\xde(\xc25\xd11\x04Yr6\xdb\x1a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4pM$<)x\xe4l,\x86\xad\xbe\xcd$n;)_\xf63\x89m\x12\x1b\xeb\xf7\x95\xf0\x00\x00\u07d4pM]\xe4\x84m9\xb5<\xd2\x1d\x1cI\xf0\x96\xdb\\\x19\xba)\x89\b=lz\xabc`\x00\x00\u07d4p]\xdd85T\x82\xb8\xc7\u04f5\x15\xbd\xa1P\r\xd7\u05e8\x17\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94pan(\x92\xfa&\x97\x05\xb2\x04k\x8f\xe3\xe7/\xa5X\x16\u04ca\x04<3\xc1\x93ud\x80\x00\x00\u07d4pg\x0f\xbb\x05\xd30\x14DK\x8d\x1e\x8ew\x00%\x8b\x8c\xaam\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\x81\xfak\xaa\xd6\u03f7\xf5\x1b,\xca\x16\xfb\x89p\x99\x1ad\xba\x89\f\xae\xc0\x05\xf6\xc0\xf6\x80\x00\xe0\x94p\x85\xae~~M\x93!\x97\xb5\u01c5\x8c\x00\xa3gF&\xb7\xa5\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4p\x86\xb4\xbd\xe3\xe3]J\xeb$\xb8%\xf1\xa2\x15\xf9\x9d\x85\xf7E\x89lh\xcc\u041b\x02,\x00\x00\u07d4p\x8a*\xf4%\u03b0\x1e\x87\xff\xc1\xbeT\xc0\xf52\xb2\x0e\xac\u0589\aE\u0503\xb1\xf5\xa1\x80\x00\u07d4p\x8e\xa7\a\xba\xe45\u007f\x1e\xbe\xa9Y\u00e2P\xac\u05aa!\xb3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794p\x8f\xa1\x1f\xe3=\x85\xad\x1b\xef\u02ee8\x18\xac\xb7\x1fj}~\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4p\x9101\x16\xd5\xf28\x9b##\x8bMej\x85\x96\u0644\u04c9;N~\x80\xaaX3\x00\x00\u07d4p\x99\xd1/n\xc6V\x89\x9b\x04\x9avW\x06]b\x99h\x92\u0209\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4p\x9f\xe9\xd2\xc1\xf1\xceB |\x95\x85\x04J`\x89\x9f5\x94/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xa05I\xaaah\xe9~\x88\xa5\b3\nZ\v\xeatq\x1a\x89Hz\x9a0E9D\x00\x00\u07d4p\xa4\x06}D\x8c\xc2]\xc8\xe7\x0ee\x1c\xea|\xf8N\x92\x10\x9e\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4p\xab4\xbc\x17\xb6o\x9c;c\xf1Q'O*r|S\x92c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4p\xc2\x13H\x8a\x02\f<\xfb9\x01N\xf5\xbad\x04rK\u02a3\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4p\xd2^\xd2\u022d\xa5\x9c\b\x8c\xf7\r\xd2+\xf2\u06d3\xac\xc1\x8a\x899GEE\u4b7c\x00\x00\u07d4p\xe5\xe9\xdas_\xf0w$\x9d\u02da\xaf=\xb2\xa4\x8d\x94\x98\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4p\xfe\xe0\x8b\x00\xc6\xc2\xc0J<b\\\x1f\xf7|\xaf\x1c2\xdf\x01\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4q\x01\xbdy\x9eA\x1c\xde\x14\xbd\xfa\xc2[\x06z\u0210\xea\xb8\xe8\x89N\x9b\x8a\xaeH\xdeG\x00\x00\u07d4q\t\xdd\x01\x1d\x15\xf3\x12-\x9d:'X\x8c\x10\xd7wDP\x8b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94q\v\x02t\xd7\x12\xc7~\b\xa5p}o>p\xc0\xce=\x92\u03ca\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794q\v\xe8\xfd^)\x18F\x8b\u2abe\xa8\r\x82\x845\u05d6\x12\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4q\x13]\x8f\x05\x96<\x90ZJ\a\x92)\t#Z\x89jR\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4q\x1e\xcfw\xd7\x1b=\x0e\xa9\\\xe4u\x8a\xfe\u0379\xc11\a\x9d\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4q!?\xca14\x04 N\u02e8q\x97t\x1a\xa9\xdf\xe9c8\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94q+vQ\x02\x14\xdcb\x0fl:\x1d\u049a\xa2+\xf6\xd2\x14\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4q/\xf77\n\x13\xed6\ts\xfe\u071f\xf5\xd2\xc9:P^\x9e\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4q3\x84:x\xd99\u019dD\x86\xe1\x0e\xbc{`*4\x9f\xf7\x89\x11\xd5\xca\xcc\xe2\x1f\x84\x00\x00\u07d4qH\xae\xf32a\xd8\x03\x1f\xac?q\x82\xff5\x92\x8d\xafT\u0649\xdeB\xee\x15D\u0750\x00\x00\u07d4qcu\x8c\xbblLR^\x04\x14\xa4\n\x04\x9d\xcc\xcc\xe9\x19\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4qh\xb3\xbb\x8c\x16s!\u067d\xb0#\xa6\xe9\xfd\x11\xaf\u026f\u0649a\t=|,m8\x00\x00\u07d4qirN\xe7\"q\xc54\xca\xd6B\x0f\xb0N\xe6D\u02c6\xfe\x89\x16<+@\u06e5R\x00\x00\u07d4qj\xd3\xc3:\x9b\x9a\n\x18\x96sW\x96\x9b\x94\xee}*\xbc\x10\x89\x1a!\x17\xfeA*H\x00\x00\xe0\x94qk\xa0\x1e\xad*\x91'\x065\xf9_%\xbf\xaf-\xd6\x10\xca#\x8a\ty\xe7\x01 V\xaax\x00\x00\u07d4qmP\u0320\x1e\x93\x85\x00\xe6B\x1c\xc0p\xc3P|g\u04c7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qv,cg\x8c\x18\xd1\xc67\x8c\xe0h\xe6f8\x13\x15\x14~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4qxL\x10Q\x17\xc1\xf6\x895y\u007f\xe1Y\xab\xc7NC\xd1j\x89l\x81\u01f3\x11\x95\xe0\x00\x00\xe0\x94qyro\\q\xae\x1bm\x16\xa6\x84(\x17Nk4\xb26F\x8a\x01\x8e\xa2P\t|\xba\xf6\x00\x00\xe0\x94q|\xf9\xbe\xab680\x8d\xed~\x19^\f\x86\x13-\x16?\xed\x8a\x032n\xe6\xf8e\xf4\"\x00\x00\u07d4q\x80\xb8>\xe5WC\x17\xf2\x1c\x80r\xb1\x91\u0615\xd4aS\u00c9\x18\xef\xc8J\xd0\u01f0\x00\x00\u07d4q\x94kq\x17\xfc\x91^\xd1\a8_B\u065d\xda\xc62I\u0089lk\x93[\x8b\xbd@\x00\x00\xe0\x94q\x9e\x89\x1f\xbc\xc0\xa3>\x19\xc1-\xc0\xf0 9\xca\x05\xb8\x01\u07ca\x01OU8F:\x1bT\x00\x00\u07d4q\xc7#\n\x1d5\xbd\u0581\x9e\u0539\xa8\x8e\x94\xa0\xeb\a\x86\u0749\uc80b5=$\x14\x00\x00\u07d4q\xd2\xccm\x02W\x8ce\xf7<W^v\u038f\xbc\xfa\xdc\xf3V\x89\x03\xec\xc0xh\x8aH\x00\x00\u07d4q\xd9INP\xc5\xddY\u0159\u06e3\x81\v\xa1u^e7\xf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4q\xe3\x8f\xf5E\xf3\x0f\xe1L\xa8c\xd4\xf5)\u007f\u050cs\xa5\u0389\xc2\x12z\xf8X\xdap\x00\x00\u07d4q\xea[\x11\xad\x8d)\xb1\xa4\xcbg\xbfX\xcal\x9f\x9c3\x8c\x16\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4q\xec:\xec?\x8f\x92!\xf9\x14\x9f\xed\xe0i\x03\xa0\xf9\xa22\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4q\xf2\xcd\u0470F\xe2\xda/\xbbZ&r4\"\xb82^%\xa3\x89\x05k9Bc\xa4\f\x00\x00\u07d4q\xfa\"\xccm3 k}p\x1a\x16:\r\xab1\xaeM1\u0589WG=\x05\u06ba\xe8\x00\x00\u07d4r\x01\xd1\xc0i \xcd9z\u8b46\x9b\u0366\xe4\u007f\xfb\x1bZ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4r\a*\x0e\xf1\xcf\xf3\xd5g\xcd\xd2`\xe7\b\xdd\xc1\x1c\xbc\x9a1\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94r\tO9Q\xff\xc9w\x1d\xce\xd2:\xda\b\v\xca\xf9\xc7\u0327\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94r\t\x94\xdb\xe5j:\x95\x92\x97t\xe2\x0e\x1f\xe5%\xcf7\x04\xe4\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4r\x0ek\"\xbfC\tf\xfa2\xb6\xac\xb9\xa5\x06\xee\xbff,a\x89\b=lz\xabc`\x00\x00\xe0\x94r\x11X\xbeWb\xb1\x19\u031b 5\xe8\x8e\xe4\xeex\U0009b08a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4r\x1f\x9d\x17\xe5\xa0\xe7B\x05\x94z\xeb\x9b\u01a7\x93\x89a\x03\x8f\x89\x02\xd0A\xd7\x05\xa2\xc6\x00\x00\u07d4r\"\xfe\xc7q\x17\x81\xd2n\xaaN\x84\x85\xf7\xaa?\xacD$\x83\x89\x18\xb8Ep\x02* \x00\x00\u07d4r9=7\xb4Q\xef\xfb\x9e\x1f\xf3\xb8U'\x12\xe2\xa9p\xd8\u00895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4r=\x8b\xaa%Q\u04ad\xdcC\xc2\x1bE\xe8\xafL\xa2\xbf\xb2\u0089_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94r@#\x00\xe8\x1d\x14l.dN+\xbd\xa1\xda\x16<\xa3\xfbV\x8a\x01{x\x83\xc0i\x16`\x00\x00\xe0\x94rH\v\xed\xe8\x1a\xd9d#\xf2\"\x8b\\a\xbeD\xfbR1\x00\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4rL\xe8X\x85~\xc5H\x1c\x86\xbd\x90n\x83\xa0H\x82\xe5\x82\x1d\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94rj\x14\xc9\x0e?\x84\x14Lv\\\xff\xac\xba>\r\xf1\x1bH\xbe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4r\x83\xcdFu\xdaX\u0116UaQ\xda\xfd\x80\xc7\xf9\x95\xd3\x18\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4r\x86\xe8\x9c\xd9\u078fz\x8a\x00\xc8o\xfd\xb59\x92\u0752Q\u0449i*\xe8\x89p\x81\xd0\x00\x00\u07d4r\x8f\x9a\xb0\x80\x15}\xb3\a1V\xdb\xca\x1a\x16\x9e\xf3\x17\x94\a\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4r\x94\xc9\x18\xb1\xae\xfbM%\x92~\xf9\u05d9\xe7\x1f\x93\xa2\x8e\x85\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94r\x94\uc763\x10\xbckK\xbd\xf5C\xb0\xefE\xab\xfc>\x1bM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4r\x9a\xadF'tNS\xf5\xd6c\t\xaatD\x8b:\xcd\xf4o\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xa2\xfc\x86u\xfe\xb9r\xfaA\xb5\r\xff\u06fa\xe7\xfa*\u07f7\x89\x9a\xb4\xfcg\xb5(\xc8\x00\x00\u07d4r\xa8&\b&)G&\xa7[\xf3\x9c\u066a\x9e\a\xa3\xea\x14\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb0Yb\xfb*\u0549\xd6Z\xd1j\"U\x9e\xba\x14X\xf3\x87\x89\a?u\u0460\x85\xba\x00\x00\u07d4r\xb5c?\xe4w\xfeT.t/\xac\xfdi\f\x13xT\xf2\x16\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4r\xb7\xa0=\xda\x14\u029cf\x1a\x1dF\x9f\xd376\xf6s\xc8\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xb9\x04D\x0e\x90\xe7 \u05ac\x1c*\u05dc2\x1d\xcc\x1c\x1a\x86\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94r\xb9\nM\xc0\x97#\x94\x92\u0179w}\xcd\x1eR\xba+\xe2\u008a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4r\xbb'\u02d9\xf3\xe2\xc2\u03d0\xa9\x8fp}0\xe4\xa2\x01\xa0q\x89X\xe7\x92n\xe8X\xa0\x00\x00\xe0\x94r\xc0\x83\xbe\xad\xbd\xc2'\xc5\xfbC\x88\x15\x97\xe3.\x83\xc2`V\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4r\xcd\x04\x8a\x11\x05tH)\x83I-\xfb\x1b\xd2yB\xa6\x96\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xd0=M\xfa\xb3P\f\xf8\x9b\x86\x86o\x15\xd4R\x8e\x14\xa1\x95\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4r\u06bb[n\ud799\xbe\x91X\x88\xf6V\x80V8\x16\b\xf8\x89\vL\x96\xc5,\xb4\xfe\x80\x00\u07d4r\xfbI\u009d#\xa1\x89P\u0132\xdc\r\xdfA\x0fS-oS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4r\xfe\xaf\x12EyR9Td[\u007f\xaf\xff\x03x\xd1\xc8$.\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\x01\xdcL\xf2mq\x86\xf2\xa1\x1b\xf8\xb0\x8b\xf2)F?d\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x04G\xf9|\xe9\xb2_\"\xba\x1a\xfb6\xdf'\xf9Xk\ub6c9,s\xc97t,P\x00\x00\u07d4s\x06\xde\x0e(\x8bV\xcf\u07d8~\xf0\xd3\xcc)f\a\x93\xf6\u0749\x1b\x8a\xbf\xb6.\xc8\xf6\x00\x00\xe0\x94s\r\x87c\u01a4\xfd\x82J\xb8\xb8Y\x16\x1e\xf7\xe3\xa9j\x12\x00\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4s\x12\x81sH\x95(\x01.v\xb4\x1a^(\u018b\xa4\xe3\xa9\u050965\u026d\xc5\u07a0\x00\x00\u07d4s\x13F\x12\bETUFTE\xa4Y\xb0l7s\xb0\xeb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s/\xea\xd6\x0f{\xfd\u05a9\xde\u0101%\xe3s]\xb1\xb6eO\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sB#\xd2\u007f\xf2>Y\x06\xca\xed\"YW\x01\xbb4\x83\f\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4sG>r\x11Q\x10\xd0\xc3\xf1\x17\b\xf8nw\xbe+\xb0\x98<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4sRXm\x02\x1a\xd0\xcfw\xe0\xe9(@JY\xf3t\xffE\x82\x89\xb8Pz\x82\a( \x00\x00\u07d4sU\v\xebs+\xa9\u076f\xdaz\xe4\x06\xe1\x8f\u007f\xeb\x0f\x8b\xb2\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4s[\x97\xf2\xfc\x1b\xd2K\x12\an\xfa\xf3\xd1(\x80s\xd2\f\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4s^2\x86f\xedV7\x14+3\x06\xb7|\xccT`\xe7,=\x89j\xb8\xf3xy\u0251\x00\x00\u07d4sc\u0350\xfb\xab[\xb8\u011a\xc2\x0f\xc6,9\x8f\xe6\xfbtL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4skDP=\xd2\xf6\xddTi\xffL[-\xb8\xeaO\xece\u0409\x11\x04\xeeu\x9f!\xe3\x00\x00\xe0\x94sk\xf1@,\x83\x80\x0f\x89>X1\x92X*\x13N\xb52\xe9\x8a\x02\x1e\x19\u0493\xc0\x1f&\x00\x00\xe0\x94s\x8c\xa9M\xb7\u038b\xe1\xc3\x05l\u0598\x8e\xb3v5\x9f3S\x8a\x05f[\x96\xcf5\xac\xf0\x00\x00\u07d4s\x91K\"\xfc/\x13\x15\x84$}\x82\xbeO\ucfd7\x8a\u053a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4s\x93'\t\xa9\u007f\x02\u024eQ\xb0\x911(e\x12#\x85\xae\x8e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4s\x93\xcb\xe7\xf9\xba!e\xe5\xa7U5\x00\xb6\xe7]\xa3\xc3:\xbf\x89\x05k\xc7^-c\x10\x00\x00\u07d4s\xb4\u0519\xde?8\xbf5\xaa\xf7i\xa6\xe3\x18\xbcm\x126\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\xbe\xddo\xda{\xa3'!\x85\b{cQ\xfc\x13=HN7\x8a\x01\x12&\xbf\x9d\xceYx\x00\x00\u07d4s\xbf\xe7q\x0f1\u02b9I\xb7\xa2`O\xbfR9\xce\xe7\x90\x15\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94s\u03c0\xae\x96\x88\xe1X\x0eh\xe7\x82\xcd\b\x11\xf7\xaaIM,\x8a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\xe0\x94s\xd7&\x9f\xf0l\x9f\xfd3uL\xe5\x88\xf7J\x96j\xbb\xbb\xba\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4s\xd8\xfe\xe3\u02c6M\xce\"\xbb&\u029c/\bm^\x95\xe6;\x8965\u026d\xc5\u07a0\x00\x00\u07d4s\xdf<>yU\xf4\xf2\xd8Y\x83\x1b\xe3\x80\x00\xb1\ak8\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4s\u48b6\f\U0010e2ef+w~\x17Z[\x1eM\f-\x8f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94t\n\xf1\xee\xfd3e\u05cb\xa7\xb1,\xb1\xa6s\xe0j\arF\x8a\x04+\xf0kx\xed;P\x00\x00\xe0\x94t\v\xfdR\xe0\x16g\xa3A\x9b\x02\x9a\x1b\x8eEWj\x86\xa2\u06ca\x03\x8e\xba\xd5\xcd\xc9\x02\x80\x00\x00\u07d4t\x0fd\x16\x14w\x9d\u03e8\x8e\xd1\xd4%\xd6\r\xb4*\x06\f\xa6\x896\"\xc6v\b\x10W\x00\x00\u07d4t\x12\u027c0\xb4\xdfC\x9f\x021\x00\xe69$\x06j\xfdS\xaf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4t\x16\x93\xc3\x03vP\x85\x13\b \xcc+c\xe9\xfa\x92\x13\x1b\x89A\rXj \xa4\xc0\x00\x00\u07d4t!\xce[\xe3\x81s\x8d\u0703\xf0&!\x97O\xf0hly\xb8\x89Xx\x8c\xb9K\x1d\x80\x00\x00\u07d4t1j\xdf%7\x8c\x10\xf5v\u0574\x1aoG\xfa\x98\xfc\xe3=\x89\x128\x13\x1e\\z\xd5\x00\x00\u07d4t6Q\xb5^\xf8B\x9d\xf5\f\xf8\x198\xc2P\x8d\xe5\u0207\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t=\xe5\x00&\xcag\xc9M\xf5O\x06b`\xe1\xd1J\xcc\x11\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tE /\ft)z\x00N\xb3rj\xa6\xa8-\xd7\xc0/\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tK\x03\xbb\xa8X*\xe5I\x8e-\xc2-\x19\x94\x94g\xabS\xfc\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4tL\fw\xba\u007f#i \xd1\xe44\xde]\xa3>H\xeb\xf0,\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4tP\xff\u007f\x99\xea\xa9\x11bu\u07ach\xe4(\xdf[\xbc\u0639\x89lk\x93[\x8b\xbd@\x00\x00\u07d4tV\u0172\xc5Cn>W\x10\b\x93?\x18\x05\xcc\xfe4\xe9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4tZ\u04eb\xc6\xee\xeb$qh\x9bS\x9ex\x9c\xe2\xb8&\x83\x06\x89=A\x94\xbe\xa0\x11\x92\x80\x00\xe0\x94tZ\xec\xba\xf9\xbb9\xb7Jg\xea\x1c\xe6#\xde6\x84\x81\xba\xa6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4t\\\xcf-\x81\x9e\u06fd\u07a8\x11{\\I\xed<*\x06n\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4tb\u021c\xaa\x9d\x8dx\x91\xb2T]\xef!otd\u057b!\x89\x05\xea\xedT\xa2\x8b1\x00\x00\u07d4td\x8c\xaa\xc7H\xdd\x13\\\xd9\x1e\xa1L(\xe1\xbdM\u007f\xf6\xae\x89\xa8\r$g~\xfe\xf0\x00\x00\xe0\x94tq\xf7.\xeb0\x06$\xeb(.\xabM\x03r<d\x9b\x1bX\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94tz\xbc\x96I\x05m9&\x04M(\u00ed\t\xed\x17\xb6}p\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4t\u007f\xf7\x94;q\xdcM\u0371f\x80x\xf8=\xd7\xccE \u0089\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94t\x80\xdeb%O+\xa8+W\x82\x19\xc0{\xa5\xbeC\r\xc3\u02ca\x01}\xa3\xa0L{>\x00\x00\x00\xe0\x94t\x84\xd2k\xec\xc1\xee\xa8\xc61^\xc3\xee\nE\x01\x17\u0706\xa0\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4t\x86:\xce\xc7]\x03\xd5>\x86\x0ed\x00/,\x16^S\x83w\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x89\u030a\xbeu\u0364\xef\r\x01\xce\xf2`^G\xed\xa6z\xb1\x89\a?u\u0460\x85\xba\x00\x00\u07d4t\x8c(^\xf1#?\xe4\xd3\x1c\x8f\xb17\x833r\x1c\x12\xe2z\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\x90\x87\xac\x0fZ\x97\xc6\xfa\xd0!S\x8b\xf1\xd6\u0361\x8e\r\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\x95\xaex\xc0\xd9\x02a\xe2\x14\x0e\xf2\x061\x04s\x1a`\xd1\xed\x89\x01\xdbPq\x89%!\x00\x00\u07d4t\x9aJv\x8b_#rH\x93\x8a\x12\xc6#\x84{\xd4\xe6\x88\u0709\x03\xe73b\x87\x14 \x00\x00\u07d4t\x9a\xd6\xf2\xb5pk\xbe/h\x9aD\u0136@\xb5\x8e\x96\xb9\x92\x89\x05k\xc7^-c\x10\x00\x00\u07d4t\xa1\u007f\x06K4N\x84\xdbce\u0695\x91\xff\x16(%vC\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4t\xae\xec\x91]\xe0\x1c\u019b,\xb5\xa65o\xee\xa1FX\xc6\u0149\f\x9a\x95\xee)\x86R\x00\x00\u07d4t\xaf\xe5I\x02\xd6\x15x%v\xf8\xba\xac\x13\xac\x97\f\x05\x0fn\x89\t\xa1\xaa\xa3\xa9\xfb\xa7\x00\x00\u07d4t\xb7\xe0\"\x8b\xae\xd6YW\xae\xbbM\x91m3:\xae\x16O\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbcJ^ E\xf4\xff\x8d\xb1\x84\xcf:\x9b\f\x06Z\xd8\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4t\xbc\xe9\xec86-l\x94\u032c&\xd5\xc0\xe1:\x8b;\x1d@\x8965&A\x04B\xf5\x00\x00\u07d4t\xbfzZ\xb5\x92\x93\x14\x9b\\`\xcf6Bc\xe5\xeb\xf1\xaa\r\x89\x06G\f>w\x1e<\x00\x00\xe0\x94t\xc7<\x90R\x8a\x15s6\xf1\xe7\xea b\n\xe5?\xd2G(\x8a\x01\xe6:.S\x8f\x16\xe3\x00\x00\u07d4t\u0464\xd0\xc7RN\x01\x8dN\x06\xed;d\x80\x92\xb5\xb6\xaf,\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94t\xd3f\xb0{/VG}|pw\xaco\xe4\x97\xe0\xebeY\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4t\xd3zQt{\xf8\xb7q\xbf\xbfC\x9493\xd1\x00\xd2\x14\x83\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xd6q\u065c\xbe\xa1\xabW\x90cu\xb6?\xf4+PE\x1d\x17\x8965\u026d\xc5\u07a0\x00\x00\u07d4t\xeb\xf4BVF\xe6\u03c1\xb1\t\xce{\xf4\xa2\xa6=\x84\x81_\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4t\xed3\xac\xf4?5\xb9\x8c\x920\xb9\xe6d.\xcbS0\x83\x9e\x89$\xf6\xdf\xfbI\x8d(\x00\x00\u07d4t\xef(i\xcb\xe6\b\x85`E\xd8\xc2\x04\x11\x18W\x9f\"6\xea\x89\x03<\xd6E\x91\x95n\x00\x00\u07d4t\xfcZ\x99\xc0\xc5F\x05\x03\xa1;\x05\tE\x9d\xa1\x9c\xe7\u0350\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4u\v\xbb\x8c\x06\xbb\xbf$\bC\xccux.\xe0/\b\xa9tS\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4u\x14\xad\xbd\xc6?H?0M\x8e\x94\xb6\u007f\xf30\x9f\x18\v\x82\x89!\u0120n-\x13Y\x80\x00\u0794u\x17\xf1l(\xd12\xbb@\xe3\xba6\u01ae\xf11\xc4b\xda\x17\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4u\x1a,\xa3Nq\x87\xc1c\u048e6\x18\xdb(\xb1<\x19m&\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94u\x1a\xbc\xb6\xcc\x030Y\x91\x18\x15\xc9o\u04516\n\xb0D-\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4u&\xe4\x82R\x9f\n\x14\xee\u0248q\xdd\xdd\x0er\x1b\f\u0662\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4u)\xf3y{\xb6\xa2\x0f~\xa6I$\x19\xc8L\x86vA\xd8\x1c\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94u*^\xe22a,\xd3\x00_\xb2n[Y}\xe1\x9fwk\xe6\x8a\x01'\xfc\xb8\xaf\xae \xd0\x00\x00\u07d4u,\x9f\xeb\xf4/f\xc4x{\xfa~\xb1|\xf53;\xbaPp\x89j\x99\xf2\xb5O\xddX\x00\x00\u07d4u930F\u07b1\xef<M\xafPa\x99\x93\xf4D\xe1\xdeh\x89@\x13\x8b\x91~\u07f8\x00\x00\u07d4uS\xaa#\xb6\x8a\xa5\xf5~\x13_\xe3\x9f\xdc#^\xac\xa8\u024c\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94uZ`\xbfR/\xbd\x8f\xff\x97#Dk~4:phV~\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4u_X~^\xff\xf7s\xa2 rj\x13\xd0\xf2\x13\r\x9f\x89k\x8965\u026d\xc5\u07a0\x00\x00\u07d4ub\x18e\xb6Y\x13e`n\xd3x0\x8c-\x1d\xefO\",\x89\xa8\r$g~\xfe\xf0\x00\x00\xe0\x94ucl\xdb\x10\x90P\xe4=]n\xc4~5\x9e!\x8e\x85~\u028a\x04\u0632'l\x89b(\x00\x00\u07d4ufIab\xbaXCw\xbe\x04\nO\x87wzpz\xca\xeb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4uk\x84\xeb\x85\xfc\xc1\xf4\xfc\xdc\u00b0\x8d\xb6\xa8n\x13_\xbc%\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4uoE\xe3\xfai4z\x9a\x97:r^<\x98\xbcM\xb0\xb5\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4u{e\x87m\xbf)\xbf\x91\x1dO\x06\x92\xa2\u027e\xb1\x13\x98\b\x89\u07d3\xa5\x937\xd6\u0740\x00\u07d4u\u007f\xa5TF\xc4`\x96\x8b\xb7K^\xbc\xa9lN\xf2\xc7\t\u01497\b\xba\xed=h\x90\x00\x00\u07d4u\x80J\xacd\xb4\x19\x90\x83\x98)\x02\x99M\x9c^\u0602\x8f\x11\x89\x1e=\a\xb0\xa6 \xe4\x00\x00\u07d4u\x92\u019d\x06{Q\xb6\xccc\x9d\x11d\xd5W\x8c`\xd2\xd2D\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4u\xab\xe5'\x0f:x\xce\x00|\xf3\u007f\x8f\xbc\x04]H\x9b{\xb1\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94u\xacTp\x17\x13L\x04\xae\x1e\x11\xd6\x0ec\xec\x04\u044d\xb4\xef\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4u\xb0\xe9\xc9B\xa4\xf0\xf6\xf8m?\x95\xff\x99\x80\"\xfag\x96;\x89P\xc5\xe7a\xa4D\b\x00\x00\xe0\x94u\xb9V\x96\xe8\xecE\x10\xd5hh\xa7\xc1\xa75\u018b$H\x90\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u07d4u\xbe\x8f\xf6^W\x88\xae\u01b2\xa5-_\xa7\xb1\xe7\xa0;\xa6u\x89\x03\xab\xcd\xc54=t\x00\x00\u07d4u\xc1\x1d\x02M\x12\xaeHl\x10\x95\xb7\xa7\xb9\u012f>\x8e\u07b9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94u\xc1\xad#\xd2?$\xb3\x84\xd0\xc3\x14\x91w\xe8f\x97a\r!\x8a\x01\\[\xcdl(\x8b\xbd\x00\x00\u07d4u\xc2\xff\xa1\xbe\xf5I\x19\xd2\t\u007fz\x14-.\x14\xf9\xb0JX\x89\x90\xf3XP@2\xa1\x00\x00\u07d4u\xd6|\xe1N\x8d)\xe8\xc2\xff\u3051{\x93\v\x1a\xff\x1a\x87\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4u\xde~\x93R\xe9\v\x13\xa5\x9aXx\xff\xec\u01c3\x1c\xacM\x82\x89\x94\x89#z\u06daP\x00\x00\u07d4u\xf7S\x9d0\x9e\x909\x98\x9e\xfe.\x8b-\xbd\x86Z\r\xf0\x88\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4v\b\xf47\xb3\x1f\x18\xbc\vd\u04c1\xae\x86\xfd\x97\x8e\u05f3\x1f\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94v\x0f\xf35N\x0f\u0793\x8d\x0f\xb5\xb8,\xef[\xa1\\=)\x16\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v\x1an6,\x97\xfb\xbd|Yw\xac\xba-\xa7F\x876_I\x89\t\xf7J\xe1\xf9S\xd0\x00\x00\u07d4v\x1el\xae\xc1\x89\xc20\xa1b\xec\x00e0\x19>g\u03dd\x19\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94v\x1f\x8a:*\U00028f7e\x1d\xa0\t2\x1f\xb2\x97d\xebb\xa1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4v)\x98\xe1\xd7R'\xfc\xedzp\xbe\x10\x9aL\vN\xd8d\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v-o0\u06b9\x915\xe4\xec\xa5\x1dRC\xd6\xc8b\x11\x02\u0549\x0fI\x89A\xe6d(\x00\x00\u07d4v3\x1e0yl\xe6d\xb2p\x0e\rASp\x0e\u0706\x97w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v8\x86\xe33\xc5o\xef\xf8[\xe3\x95\x1a\xb0\xb8\x89\xce&.\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v:|\xba\xb7\rzd\u0427\xe5)\x80\xf6\x81G%\x93I\f\x89 \x86\xac5\x10R`\x00\x00\u07d4v>\xec\u0c0a\u021e2\xbf\xa4\xbe\xcev\x95\x14\xd8\xcb[\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4v@\xa3\u007f\x80R\x98\x15\x15\xbc\xe0x\u0693\xaf\xa4x\x9bW4\x89lk\x93[\x8b\xbd@\x00\x00\u0794vA\xf7\xd2j\x86\xcd\xdb+\xe10\x81\x81\x0e\x01\xc9\xc8<K \x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4vF\x92\xcc\xcb3@]\u042b\f3y\xb4\x9c\xaf\x8eb!\xba\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4vMR\x12&:\xffJ*\x14\xf01\xf0N\xc7I\u0708>E\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94vO\xc4mB\x8bm\xbc\"\x8a\x0f_U\xc9P\x8cw.\xab\x9f\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4vPn\xb4\xa7\x80\xc9Q\xc7J\x06\xb0=;\x83b\xf0\x99\x9dq\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94v[\xe2\xe1/b\x9ecI\xb9}!\xb6*\x17\xb7\xc80\xed\xab\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94vb\x81P\xe2\x99[['\x9f\xc8>\r\xd5\xf1\x02\xa6q\xdd\x1c\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4vk7Y\xe8yN\x92m\xacG=\x91:\x8f\xb6\x1a\xd0\xc2\u0249\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4vp\xb0/,<\xf8\xfdOG0\xf38\x1aq\xeaC\x1c3\u01c9\x0e~\xeb\xa3A\vt\x00\x00\u07d4vz\x03eZ\xf3`\x84\x1e\x81\r\x83\xf5\xe6\x1f\xb4\x0fL\xd1\x13\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4vz\u0190y\x1c.#E\x10\x89\xfelp\x83\xfeU\u07b6+\x89,s\xc97t,P\x00\x00\u07d4v\u007f\xd7y}Qi\xa0_sd2\x1c\x19\x84:\x8c4\x8e\x1e\x89\x01\x04\xe7\x04d\xb1X\x00\x00\u0794v\x84o\r\xe0;Zv\x97\x1e\xad)\x8c\xdd\b\x84:K\xc6\u0188\xd7\x1b\x0f\u088e\x00\x00\xe0\x94v\x84\x98\x93N7\xe9\x05\xf1\xd0\xe7{D\xb5t\xbc\xf3\xecJ\xe8\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4v\x8c\xe0\u06a0)\xb7\xde\xd0\"\xe5\xfcWM\x11\xcd\xe3\xec\xb5\x17\x89\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\xe0\x94v\x93\xbd\xebo\xc8+[\xcar\x13U\"1u\xd4z\bKM\x8a\x04\xa8\x9fT\xef\x01!\xc0\x00\x00\u07d4v\xaa\xf8\xc1\xac\x01/\x87R\xd4\xc0\x9b\xb4f\a\xb6e\x1d\\\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4v\xab\x87\xddZ\x05\xad\x83\x9aN/\xc8\xc8Z\xa6\xba\x05d\x170\x89lk\x93[\x8b\xbd@\x00\x00\u07d4v\xaf\xc2%\xf4\xfa0}\xe4\x84U+\xbe\x1d\x9d?\x15\aLJ\x89\xa2\x90\xb5\u01ed9h\x00\x00\xe0\x94v\xbe\xca\xe4\xa3\x1d6\xf3\xcbW\u007f*CYO\xb1\xab\xc1\xbb\x96\x8a\x05C\xa9\xce\x0e\x132\xf0\x00\x00\u07d4v\xc2u5\xbc\xb5\x9c\xe1\xfa-\x8c\x91\x9c\xab\xebJk\xba\x01\u0449lk\x93[\x8b\xbd@\x00\x00\u07d4v\xca\"\xbc\xb8y\x9eS'\u012a*}\tI\xa1\xfc\xce_)\x89R\xa0?\"\x8cZ\xe2\x00\x00\u07d4v\xca\u0108\x11\x1aO\u0555\xf5h\xae:\x85\x87p\xfc\x91]_\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94v\u02dc\x8bi\xf48vu\u0102S\xe24\xcb~\rt\xa4&\x8a\x01\x90\xf4H.\xb9\x1d\xae\x00\x00\u07d4v\xf8:\xc3\xda0\xf7\t&(\xc73\x9f \x8b\xfc\x14,\xb1\ue25a\x18\xff\xe7B}d\x00\x00\xe0\x94v\xf9\xad=\x9b\xbd\x04\xae\x05\\\x14w\xc0\xc3^u\x92\xcb* \x8a\b\x83?\x11\xe3E\x8f \x00\x00\u07d4v\xff\xc1W\xadk\xf8\xd5m\x9a\x1a\u007f\u077c\x0f\xea\x01\n\xab\xf4\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\x02\x8e@\x9c\xc4:;\xd3=!\xa9\xfcS\xec`n\x94\x91\x0e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4w\f/\xb2\u0128\x17S\xac\x01\x82\xeaF\x0e\xc0\x9c\x90\xa5\x16\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\r\x98\xd3\x1bCS\xfc\xee\xe8V\fL\u03c0>\x88\xc0\xc4\xe0\x89 \x86\xac5\x10R`\x00\x00\xe0\x94w\x13\xab\x807A\x1c\t\xbah\u007fo\x93d\xf0\xd3#\x9f\xac(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4w\x15\a\xae\xeej%]\xc2\u035d\xf5QT\x06-\b\x97\xb2\x97\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4w\x19\x88\x87\x95\xadtY$\xc7W`\u0771\x82}\xff\xd8\u0368\x89lkLM\xa6\u077e\x00\x00\u07d4w'\xaf\x10\x1f\n\xab\xa4\xd2:\x1c\xaf\xe1|n\xb5\u06b1\xc6\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4w,)\u007f\n\u0454H.\xe8\xc3\xf06\xbd\xeb\x01\xc2\x01\xd5\u0309\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94w0o\xfe.J\x8f<\xa8&\xc1\xa2I\xf7!-\xa4:\xef\xfd\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4w1A\x12}\x8c\xf3\x18\xae\xbf\x886Z\xdd=U'\xd8[j\x8966\u05ef^\u024e\x00\x00\u07d4wF\xb6\xc6i\x9c\x8f4\xca'h\xa8 \xf1\xff\xa4\xc2\a\xfe\x05\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4wQ\xf3c\xa0\xa7\xfd\x053\x19\b\t\u076f\x93@\xd8\xd1\x12\x91\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4wW\xa4\xb9\xcc=\x02G\u032a\xeb\x99\t\xa0\xe5n\x1d\xd6\xdc\u0089\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4w\\\x10\xc9>\r\xb7 [&CE\x823\xc6O\xc3?\xd7[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4wa~\xbcK\xeb\xc5\xf5\xdd\xeb\x1bzp\xcd\xebj\xe2\xff\xa0$\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4wiC\xff\xb2\xef\\\xdd5\xb8<(\xbc\x04k\xd4\xf4gp\x98\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94wp\x1e,I=\xa4|\x1bX\xf4!\xb5I]\xeeE\xbe\xa3\x9b\x8a\x01H\xf6I\xcfaB\xa5\x80\x00\u07d4wy\x8f \x12W\xb9\xc3R\x04\x95pW\xb5Ft\xae\xfaQ\u07c9\b\x13\xcaV\x90m4\x00\x00\u07d4w\x8cC\xd1\x1a\xfe;Xo\xf3t\x19-\x96\xa7\xf2=+\x9b\u007f\x89\x8b\xb4\xfc\xfa;}k\x80\x00\u07d4w\x8cy\xf4\xde\x19S\xeb\u0398\xfe\x80\x06\xd5:\x81\xfbQ@\x12\x8963\x03\"\xd5#\x8c\x00\x00\u07d4w\x92t\xbf\x18\x03\xa36\xe4\u04f0\r\u0753\xf2\xd4\xf5\xf4\xa6.\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xa1q\"\xfa1\xb9\x8f\x17\x11\xd3*\x99\xf0>\xc3&\xf3=\b\x89\\(=A\x03\x94\x10\x00\x00\u07d4w\xa3I\a\xf3\x05\xa5L\x85\xdb\t\xc3c\xfd\xe3\xc4~j\xe2\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4w\xa7i\xfa\xfd\xec\xf4\xa68v-[\xa3\x96\x9d\xf61 \xa4\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xbekd\xd7\xc73\xa46\xad\xec^\x14\xbf\x9a\xd7@+\x1bF\x8965\u026d\xc5\u07a0\x00\x00\u07d4w\xbf\xe9<\u0367P\x84~A\xa1\xaf\xfe\xe6\xb2\u0696\xe7!N\x89\x10CV\x1a\x88)0\x00\x00\u07d4w\u0126\x97\xe6\x03\xd4+\x12\x05l\xbb\xa7a\xe7\xf5\x1d\x04C\xf5\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4w\xcc\x02\xf6#\xa9\u03d8S\t\x97\xeag\xd9\\;I\x18Y\xae\x89Is\x03\xc3n\xa0\xc2\x00\x00\u07d4w\xd4?\xa7\xb4\x81\xdb\xf3\xdbS\f\xfb\xf5\xfd\xce\xd0\xe6W\x181\x89lk\x93[\x8b\xbd@\x00\x00\u07d4w\xda^lr\xfb6\xbc\xe1\xd9y\x8f{\xcd\xf1\u044fE\x9c.\x89\x016\x95\xbbl\xf9>\x00\x00\u07d4w\xf4\xe3\xbd\xf0V\x88<\xc8r\x80\xdb\xe6@\xa1\x8a\r\x02\xa2\a\x89\n\x81\x99:+\xfb[\x00\x00\u0794w\xf6\t\u0287 \xa0#&,U\xc4o-&\xfb90\xaci\x88\xf0\x15\xf2W6B\x00\x00\u07d4w\xf8\x1b\x1b&\xfc\x84\xd6\u0797\uf2df\xbdr\xa310\xccJ\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\x19\xb0E\x8e1N+S\xbf\xe0\f8I_\u0539\xfd\xf8\u0589\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4x\x1b\x15\x01dz.\x06\xc0\xedC\xff\x19\u007f\xcc\xec5\xe1p\v\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4x/R\xf0\xa6v\xc7w\x16\xd5t\xc8\x1e\xc4hO\x9a\x02\n\x97\x89.\x14\xe2\x06\xb70\xad\x80\x00\u07d4x5]\xf0\xa20\xf8=\x03,p1TAM\xe3\xee\u06b5W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x6\xf7\xefk\u01fd\x0f\xf3\xac\xafD\x9c\x84\xddk\x1e,\x93\x9f\x89\xe0\x8d\xe7\xa9,\xd9|\x00\x00\u07d4x7\xfc\xb8v\xda\x00\xd1\xeb;\x88\xfe\xb3\xdf?\xa4\x04/\xac\x82\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4x>\uc2a5\xda\xc7{.f#\xedQ\x98\xa41\xab\xba\xee\a\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4x\\\x8e\xa7t\xd70D\xa74\xfay\n\x1b\x1et>w\xed|\x89\f\xf1Rd\f\\\x83\x00\x00\u07d4x`\xa3\xde8\xdf8*\xe4\xa4\xdc\xe1\x8c\f\a\xb9\x8b\xce=\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94xcCq\xe1s\x04\xcb\xf39\xb1E*L\xe48\xdcvL\u038a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4xd\u0719\x9f\xe4\xf8\xe0\x03\xc0\xf4=\xec\u00da\xae\x15\"\xdc\x0f\x89\x05\x1e\x10+\xd8\xec\xe0\x00\x00\u07d4xtj\x95\x8d\xce\xd4\xc7d\xf8vP\x8cAJh4,\uce49\x02\xbe7O\xe8\xe2\xc4\x00\x00\xe0\x94x}1?\xd3k\x05>\xee\xae\xdb\xcet\xb9\xfb\x06x32\x89\x8a\x05\xc0X\xb7\x84'\x19`\x00\x00\u07d4x\x85\x9c[T\x8bp\r\x92\x84\xce\xe4\xb6c</R\xe5)\u0089\xa00\xdc\xeb\xbd/L\x00\x00\xe0\x94x\x8e\x80\x97A\xa3\xb1J\"\xa4\xb1\xd97\xc8,\xfe\xa4\x89\uef8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4x\xa1\xe2T@\x9f\xb1\xb5Z|\xb4\u074e\xba;0\u023a\xd9\xef\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94x\xa5\xe8\x99\x00\xbd?\x81\xddq\xba\x86\x9d%\xfe\xc6Ra\xdf\x15\x8a\n\xfd\x81/\xee\x03\xd5p\x00\x00\xe0\x94x\xb9x\xa9\xd7\xe9\x1e\xe5)\xeaO\u0137o\xea\xf8v/i\x8c\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\xe0\x94x\xce>=GJ\x8a\x04{\x92\xc4\x15B$-\n\b\xc7\x0f\x99\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4x\u03c36\xb3(\xdb=\x87\x81:G+\x9e\x89\xb7^\f\xf3\xbc\x8965\u026d\xc5\u07a0\x00\x00\u07d4x\xd4\xf8\xc7\x1c\x1eh\xa6\x9a\x98\xf5/\xcbE\u068a\xf5n\xa1\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4x\xdf&\x81\xd6\xd6\x02\xe2!B\xd5A\x16\u07a1]EIW\xaa\x89\x10'\x94\xad \xdah\x00\x00\u07d4x\xe0\x8b\xc53A<&\u2473\x14?\xfa|\u026f\xb9{x\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4x\xe8?\x80\xb3g\x8cz\nN>\x8c\x84\xdc\xcd\xe0dBbw\x89a\t=|,m8\x00\x00\u07d4x\xf5\xc7G\x85\xc5f\x8a\x83\x80r\x04\x8b\xf8\xb4SYM\u06ab\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\x0f\x91\xbd]\x1c\\\xc4s\x9a\xe9\x13\x00\u06c9\xe1\xc10<\x93\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\x17\u5f42\xa9y\x0f\xd6P\xd0C\xcd\xd90\xf7y\x963\u06c9\xd8\xd4`,&\xbfl\x00\x00\u07d4y\x19\xe7b\u007f\x9b}T\xea;\x14\xbbM\xd4d\x9fO9\xde\xe0\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4y\x1f`@\xb4\xe3\xe5\r\xcf5S\xf1\x82\u0357\xa9\x060\xb7]\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4y0\xc2\xd9\xcb\xfa\x87\xf5\x10\xf8\xf9\x87w\xff\x8a\x84H\xcaV)\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4yE)\u041d\x01rq5\x970\x02pu\xb8z\xd8=\xaen\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4yKQ\u00deS\xd9\xe7b\xb0a;\x82\x9aD\xb4r\xf4\xff\xf3\x89$5\xe0dxA\u0300\x00\xe0\x94yU\x1c\xed\xe3v\xf7G\xe3ql\x8dy@\rvm.\x01\x95\x8a\t\xcb7\xaf\xa4\xffxh\x00\x00\u07d4y^\xbc&&\xfc9\xb0\xc8b\x94\xe0\xe87\xdc\xf5#U0\x90\x8965\u026d\xc5\u07a0\x00\x00\u07d4yn\xbb\xf4\x9b>6\xd6v\x94\xady\xf8\xff6vz\xc6\xfa\xb0\x89\x03K\xc4\xfd\xde'\xc0\x00\x00\u07d4yo\x87\xbaaz)0\xb1g\v\xe9.\xd1(\x1f\xb0\xb3F\xe1\x89\x06\xf5\xe8o\xb5((\x00\x00\u07d4yt'\xe3\xdb\xf0\xfe\xaez%\x06\xf1-\xf1\xdc@2n\x85\x05\x8965\u026d\xc5\u07a0\x00\x00\u07d4yu\x10\xe3\x86\xf5c\x93\xce\xd8\xf4w7\x8aDLHO}\xad\x8965\u026d\xc5\u07a0\x00\x00\u07d4y{\xb7\xf1W\xd9\xfe\xaa\x17\xf7m\xa4\xf7\x04\xb7M\xc1\x03\x83A\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4y\x88\x90\x131\xe3\x87\xf7\x13\xfa\u03b9\x00\\\xb9\xb6Q6\xeb\x14\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4y\x89\u041f8&\xc3\u5bccu*\x81\x15r:\x84\xd8\tp\x89\x16\x86\xf8aL\xf0\xad\x00\x00\xe0\x94y\x95\xbd\x8c\xe2\xe0\xc6{\xf1\u01e51\xd4w\xbc\xa1\xb2\xb9ua\x8a\x01BH\xd6\x17\x82\x9e\xce\x00\x00\u07d4y\xae\xb3Ef\xb9t\xc3ZX\x81\xde\xc0 \x92}\xa7\xdf]%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xb1 \xeb\x88\x06s#!(\x8fgZ'\xa9\"_\x1c\xd2\ub245\xa0\xbf7\xde\xc9\xe4\x00\x00\u07d4y\xb4\x8d-a7\u00c5Ma\x1c\x01\xeaBBz\x0fY{\xb7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4y\xb8\xaa\xd8y\xdd0V~\x87x\xd2\xd21\xc8\xf3z\xb8sN\x89lk\x93[\x8b\xbd@\x00\x00\u07d4y\xbf/{n2\x8a\xaf&\xe0\xbb\t?\xa2-\xa2\x9e\xf2\xf4q\x89a\t=|,m8\x00\x00\u07d4y\xc10\xc7b\xb8v[\x19\u04ab\u0260\x83\xab\x8f:\xady@\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4y\xc1\xbe\x19q\x1fs\xbe\xe4\xe61j\xe7T\x94Y\xaa\u03a2\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4y\xc6\x00/\x84R\xca\x15\u007f\x13\x17\xe8\n/\xaf$GUY\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4y\xca\xc6IO\x11\xef'\x98t\x8c\xb52\x85\xbd\x8e\"\xf9|\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4y\u03e9x\n\xe6\xd8{,1\x88?\t'i\x86\u021ag5\x8965\u026d\xc5\u07a0\x00\x00\u07d4y\u06e2VG-\xb4\xe0X\xf2\xe4\xcd\xc3\xeaN\x8aBw83\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4y\xed\x10\xcf\x1fm\xb4\x82\x06\xb5\t\x19\xb9\xb6\x97\b\x1f\xbd\xaa\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xf0\x8e\x01\xce\t\x88\xe6<\u007f\x8f)\b\xfa\xdeC\xc7\xf9\xf5\u0248\xfc\x93c\x92\x80\x1c\x00\x00\u07d4y\xfdmH1Pf\xc2\x04\xf9e\x18i\xc1\tl\x14\xfc\x97\x81\x89lk\x93[\x8b\xbd@\x00\x00\u0794y\xff\xb4\xac\x13\x81*\vx\u0123{\x82u\">\x17k\xfd\xa5\x88\xf0\x15\xf2W6B\x00\x00\u07d4z\x05\x89\xb1C\xa8\xe5\xe1\a\u026cf\xa9\xf9\xf8Yz\xb3\u7ac9Q\xe92\xd7n\x8f{\x00\x00\u07d4z\nx\xa9\xcc9?\x91\xc3\xd9\xe3\x9ak\x8c\x06\x9f\a^k\xf5\x89Hz\x9a0E9D\x00\x00\u07d4z\x13p\xa7B\xec&\x87\xe7a\xa1\x9a\u0167\x942\x9e\xe6t\x04\x89\xa2\xa12ga\xe2\x92\x00\x00\xe0\x94z-\xfcw\x0e$6\x811\xb7\x84w\x95\xf2\x03\xf3\xd5\r[V\x8a\x02i\xfe\xc7\xf06\x1d \x00\x00\u07d4z3\x83N\x85\x83s>-R\xae\xadX\x9b\u046f\xfb\x1d\xd2V\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94z6\xab\xa5\xc3\x1e\xa0\xca~'{\xaa2\xecF\u0393\xcfu\x06\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94z8\x11\"\xba\xday\x1az\xb1\xf6\x03}\xac\x80C'S\xba\xad\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94zH\xd8w\xb6:\x8f\x8f\x93\x83\xe9\xd0\x1eS\xe8\fR\x8e\x95_\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4zO\x9b\x85\x06\x90\xc7\xc9F\x00\xdb\xee\f\xa4\xb0\xa4\x11\xe9\xc2!\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4zc\x86\x9f\xc7g\xa4\u01b1\xcd\x0e\x06I\xf3cL\xb1!\xd2K\x89\x043\x87Oc,\xc6\x00\x00\u07d4zg\xdd\x04:PO\xc2\xf2\xfcq\x94\xe9\xbe\xcfHL\xec\xb1\xfb\x89\r\x8drkqw\xa8\x00\x00\xe0\x94zk&\xf48\u0663RD\x91U\xb8\x87l\xbd\x17\xc9\u065bd\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4zmx\x1cw\u013a\x1f\xca\xdfhsA\xc1\xe3\x17\x99\xe9='\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4zph\xe1\xc37\\\x0eY\x9d\xb1\xfb\xe6\xb2\xea#\xb8\xf4\a\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4zt\xce\xe4\xfa\x0fcp\xa7\x89O\x11l\xd0\f\x11G\xb8>Y\x89+^:\xf1k\x18\x80\x00\x00\u07d4zy\xe3\x0f\xf0W\xf7\n=\x01\x91\xf7\xf5?v\x157\xaf}\xff\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94zzO\x80sW\xa4\xbb\xe6\x8e\x1a\xa8\x0692\x10\xc4\x11\u0333\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4z\x85c\x86y\x01 o?+\xf0\xfa>\x1c\x81\t\u02bc\u0345\x89\amA\xc6$\x94\x84\x00\x00\xe0\x94z\x87\x97i\n\xb7{Tp\xbf|\f\x1b\xbaa%\b\xe1\xac}\x8a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4z\x8c\x89\xc0\x14P\x9dV\u05f6\x810f\x8f\xf6\xa3\xec\xecsp\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94z\x94\xb1\x99\x92\u03b8\xcec\xbc\x92\xeeKZ\xde\xd1\fM\x97%\x8a\x03\x8d\x1a\x80d\xbbd\xc8\x00\x00\u07d4z\xa7\x9a\xc0C\x16\u030d\b\xf2\x00e\xba\xa6\xd4\x14(\x97\xd5N\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4z\xadM\xbc\u04ec\xf9\x97\u07d3XiV\xf7+d\u062d\x94\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94z\xb2V\xb2\x04\x80\n\xf2\x017\xfa\xbc\xc9\x16\xa22Xu%\x01\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\xbaV\xf6:H\xbc\b\x17\u05b9p9\x03\x9az\xd6/\xae.\x89 \x86\xac5\x10R`\x00\x00\xe0\x94z\xbb\x10\xf5\xbd\x9b\xc3;\x8e\xc1\xa8-d\xb5[k\x18wuA\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4z\u010d@\xc6d\u031am\x89\xf1\xc5\xf5\xc8\n\x1cp\xe7D\u6263\x10b\xbe\xee\xd7\x00\x00\x00\u07d4z\u014fo\xfcO\x81\a\xaen07\x8eN\x9f\x99\xc5\u007f\xbb$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4z\xd3\xf3\aao\x19\u0731C\xe6DM\xab\x9c<3a\x1fR\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4z\xd8,\xae\xa1\xa8\xb4\xed\x051\x9b\x9c\x98p\x17<\x81N\x06\xee\x89!d\xb7\xa0J\u0220\x00\x00\u07d4z\xde]f\xb9D\xbb\x86\f\x0e\xfd\xc8bv\u054fFS\xf7\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xdf\xed\xb0m\x91\xf3\xccs\x90E\v\x85U\x02p\x88<{\xb7\x89\x11x\xfa@Q]\xb4\x00\x00\u07d4z\xe1\xc1\x9eS\xc7\x1c\xeeLs\xfa\xe2\xd7\xfcs\xbf\x9a\xb5\u348965\u026d\xc5\u07a0\x00\x00\u07d4z\xe6Y\xeb;\xc4hR\xfa\x86\xfa\xc4\xe2\x1cv\x8dP8\x89E\x89\x0f\x81\f\x1c\xb5\x01\xb8\x00\x00\u07d4z\xea%\xd4+&\x12(n\x99\xc56\x97\u01bcA\x00\xe2\u06ff\x89lk\x93[\x8b\xbd@\x00\x00\u07d4z\xef{U\x1f\v\x9cF\xe7U\xc0\xf3\x8e[:s\xfe\x11\x99\xf5\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4{\v1\xffn$t^\xad\x8e\u067b\x85\xfc\v\xf2\xfe\x1dU\u0509+^:\xf1k\x18\x80\x00\x00\xe0\x94{\x0f\xea\x11v\xd5!Y3:\x14<)IC\xda6\xbb\u0774\x8a\x01\xfc}\xa6N\xa1L\x10\x00\x00\u07d4{\x11g<\xc0\x19bk)\f\xbd\xce&\x04o~m\x14\x1e!\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x12!b\xc9\x13\xe7\x14l\xad\v~\xd3z\xff\xc9*\v\xf2\u007f\x89Q\xaf\tk#\x01\u0440\x00\u07d4{\x1b\xf5:\x9c\xbe\x83\xa7\u07a44W\x9f\xe7*\xac\x8d*\f\u0409\n\xd4\xc81j\v\f\x00\x00\u07d4{\x1d\xaf\x14\x89\x1b\x8a\x1e\x1b\xd4)\u0633k\x9aJ\xa1\u066f\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4{\x1f\xe1\xabM\xfd\x00\x88\xcd\xd7\xf6\x01c\xefY\xec*\xee\x06\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4{%\xbb\x9c\xa8\xe7\x02!~\x933\"RP\xe5<6\x80MH\x89e\xea=\xb7UF`\x00\x00\u07d4{'\xd0\xd1\xf3\xdd<\x14\x02\x94\xd0H\x8bx>\xbf@\x15'}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94{@\a\xc4^ZW?\u06f6\xf8\xbdtk\xf9J\xd0J<&\x8a\x038!\xf5\x13]%\x9a\x00\x00\u07d4{C\xc7\xee\xa8\xd6#U\xb0\xa8\xa8\x1d\xa0\x81\xc6Dk3\xe9\xe0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4{M*8&\x90i\xc1\x85Ww\rY\x1d$\xc5\x12\x1f^\x83\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94{au\xec\x9b\xef\xc78$\x955\xdd\xde4h\x8c\xd3n\xdf%\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94{f\x12hy\x84M\xfa4\xfee\xc9\xf2\x88\x11\u007f\xef\xb4I\xad\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4{j\x84q\x8d\xd8nc3\x84)\xac\x81\x1d|\x8a\x86\x0f!\xf1\x89a\t=|,m8\x00\x00\xe0\x94{q,z\xf1\x16v\x00jf\xd2\xfc\\\x1a\xb4\xc4y\xce`7\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4{s$-u\u029a\xd5X\xd6P)\r\xf1v\x92\xd5L\u0638\x89lnY\xe6|xT\x00\x00\u07d4{v\x1f\xeb\u007f\u03e7\xde\xd1\xf0\xeb\x05\x8fJ`\v\xf3\xa7\b\u02c9\xf9]\xd2\xec'\xcc\xe0\x00\x00\xe0\x94{\x82|\xae\u007f\xf4t\t\x18\xf2\xe00\xab&\u02d8\xc4\xf4l\xf5\x8a\x01\x94hL\v9\xde\x10\x00\x00\xe0\x94{\x892\x86B~r\xdb!\x9a!\xfcM\xcd_\xbfY(<1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4{\x92&\xd4o\xe7Q\x94\v\xc4\x16\xa7\x98\xb6\x9c\xcf\r\xfa\xb6g\x89\u3bb5sr@\xa0\x00\x00\u07d4{\x98\xe2<\xb9k\xee\xe8\n\x16\x80i\ube8f \xed\xd5\\\u03c9\v\xa0\xc9\x15\x87\xc1J\x00\x00\u07d4{\xb0\xfd\xf5\xa6c\xb5\xfb\xa2\x8d\x9c\x90*\xf0\xc8\x11\xe2R\xf2\x98\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4{\xb9W\x1f9K\v\x1a\x8e\xbaVd\xe9\u0635\xe8@g{\xea\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94{\xb9\x84\xc6\u06f9\xe2y\x96j\xfa\xfd\xa5\x9c\x01\xd0&'\xc8\x04\x8a\x01\xb4d1\x1dE\xa6\x88\x00\x00\u07d4{\xbb\xec^p\xbd\xea\u063b2\xb4(\x05\x98\x8e\x96H\xc0\xaa\x97\x8966\u05ef^\u024e\x00\x00\u07d4{\xca\x1d\xa6\xc8\nf\xba\xa5\xdbZ\u0245A\u013e'kD}\x89$\xcf\x04\x96\x80\xfa<\x00\x00\u07d4{\u0772\xee\x98\xde\x19\xeeL\x91\xf6a\xee\x8eg\xa9\x1d\x05K\x97\x8965\u026d\xc5\u07a0\x00\x00\u0794{\xe2\xf7h\f\x80-\xa6\x15L\x92\xc0\x19J\xe72Qzqi\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4{\xe7\xf2Eiq\x88;\x9a\x8d\xbeL\x91\xde\xc0\x8a\xc3N\x88b\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4{\xe8\u0334\xf1\x1bf\xcan\x1dW\xc0\xb59b!\xa3\x1b\xa5:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94{\xeb\x81\xfb/^\x91Rk*\xc9y^v\u019b\xcf\xf0K\xc0\x8a\x0e\xb2.yO\n\x8d`\x00\x00\u07d4|\b\x83\x05L-\x02\xbcz\x85+\x1f\x86\xc4'w\xd0\xd5\xc8V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\x0f^\a C\xc9\xeet\x02B\x19~x\xccK\x98\xcd\xf9`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|\x1d\xf2JO\u007f\xb2\u01f4r\xe0\xbb\x00l\xb2}\xcd\x16AV\x8965\u026d\xc5\u07a0\x00\x00\u07d4|)\xd4}W\xa73\xf5k\x9b!pc\xb5\x13\xdc;1Y#\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4|+\x96\x03\x88JO.FN\u03b9}\x17\x93\x8d\x82\x8b\xc0,\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4|8,\x02\x96a.N\x97\xe4@\xe0-8q';U\xf5;\x89\n\xb6@9\x12\x010\x00\x00\u07d4|>\xb7\x13\xc4\xc9\xe08\x1c\xd8\x15L|\x9a}\xb8d\\\xde\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|D\x01\xae\x98\xf1.\xf6\xde9\xae$\u03df\xc5\x1f\x80\xeb\xa1k\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|E\xf0\xf8D*V\xdb\u04dd\xbf\x15\x99\x95A\\R\xedG\x9b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|S-\xb9\xe0\xc0l&\xfd@\xac\xc5j\xc5\\\x1e\xe9-<:\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4|`\xa0_zJ_\x8c\xf2xC\x916.uZ\x83A\xefY\x89f\x94\xf0\x18*7\xae\x00\x00\u07d4|`\xe5\x1f\v\xe2(\xe4\xd5o\xdd)\x92\xc8\x14\xdaw@\u01bc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4|i$\xd0|>\xf5\x89\x19f\xfe\nxV\xc8{\xef\x9d 4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94|\x8b\xb6Zo\xbbI\xbdA3\x96\xa9\xd7\xe3\x10S\xbb\xb3z\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94|\x9a\x11\f\xb1\x1f%\x98\xb2\xb2\x0e,\xa4\x002^A\xe9\xdb3\x8a\x05\x81v{\xa6\x18\x9c@\x00\x00\u07d4|\xbc\xa8\x8f\xcaj\x00`\xb9`\x98\\\x9a\xa1\xb0%4\xdc\"\b\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4|\xbe\xb9\x992\xe9~n\x02\x05\x8c\xfcb\u0432k\xc7\u0325+\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xc2Jj\x95\x8c \xc7\xd1$\x96`\xf7Xb&\x95\v\r\x9a\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4|\xd2\x0e\u0335\x18\xb6\f\xab\t[r\x0fW\x15p\u02aaD~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4|\xd5\xd8\x1e\xab7\xe1\x1ebv\xa3\xa1\t\x12Q`~\r~8\x89\x03hM^\xf9\x81\xf4\x00\x00\u07d4|\xdft!9E\x95=\xb3\x9a\xd0\xe8\xa9x\x1a\xddy.M\x1d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4|\xe4hdF\U000547be\xd6r\x15\xeb\rZ\x1d\xd7,\x11\xb8\x89x9\xd3!\xb8\x1a\xb8\x00\x00\u07d4|\xefMC\xaaA\u007f\x9e\xf8\xb7\x87\xf8\xb9\x9dS\xf1\xfe\xa1\ue209g\x8a\x93 b\xe4\x18\x00\x00\u07d4}\x03P\xe4\v3\x8d\xdasfa\x87+\xe3?\x1f\x97R\xd7U\x89\x02\xb4\xf5\xa6\U00051500\x00\xe0\x94}\x04\xd2\xed\xc0X\xa1\xaf\xc7a\xd9\u025a\xe4\xfc\\\x85\xd4\u0226\x8aB\xa9\xc4g\\\x94g\xd0\x00\x00\u07d4}\v%^\xfbW\xe1\x0fp\b\xaa\"\xd4\x0e\x97R\xdf\xcf\x03x\x89\x01\x9f\x8euY\x92L\x00\x00\xe0\x94}\x13\xd6pX\x84\xab!W\u074d\xccpF\xca\xf5\x8e\xe9K\xe4\x8a\x1d\r\xa0|\xbb>\xe9\xc0\x00\x00\u07d4}'>c~\xf1\xea\u0101\x11\x94\x13\xb9\x1c\x98\x9d\xc5\xea\xc1\"\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4}*R\xa7\xcf\f\x846\xa8\xe0\a\x97kl&\xb7\"\x9d\x1e\x15\x89\x17\xbf\x06\xb3*$\x1c\x00\x00\u07d4}4\x805i\xe0\v\u05b5\x9f\xff\b\x1d\xfa\\\n\xb4\x19zb\x89\\\xd8|\xb7\xb9\xfb\x86\x00\x00\u07d4}4\xffY\xae\x84\nt\x13\u01baL[\xb2\xba,u\xea\xb0\x18\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4}9(R\xf3\xab\xd9/\xf4\xbb[\xb2l\xb6\bt\xf2\xbeg\x95\x8966\xc2^f\xec\xe7\x00\x00\u07d4}DRg\u015a\xb8\u04a2\xd9\xe7\t\x99\x0e\th%\x80\u011f\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94}U\x13\x97\xf7\x9a)\x88\xb0d\xaf\xd0\xef\xeb\xee\x80,w!\xbc\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\u07d4}Z\xa3?\xc1KQ\x84\x1a\x06\x90n\xdb+\xb4\x9c*\x11ri\x89\x10D\x00\xa2G\x0eh\x00\x00\xe0\x94}]/s\x94\x9d\xad\xda\bV\xb2\x06\x98\x9d\xf0\a\x8dQ\xa1\xe5\x8a\x02<upr\xb8\xdd\x00\x00\x00\xe0\x94}n\x99\r\xaaq\x05\xde%&3\x983\xf7{\\\v\x85\xd8O\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4}s\x8608\xcc\xca\"\xf9j\xff\xda\x10InQ\xe1\xe6\xcdH\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4}}\xd5\xeeaM\xbbo\xbf\xbc\xd2c\x05$z\x05\x8cA\xfa\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4}~|aw\x9a\xdbw\x06\xc9M2@\x9a+\xb4\u953f`\x89.\xf2\r\x9f\xc7\x1a\x14\x00\x00\u07d4}\x82\xe5#\xcc-\u0151\xda9T\u8dbb,\xafda\u6709}\x8d\xc2\xef\xff\xb1\xa9\x00\x00\u07d4}\x85\x84\x93\xf0t\x15\xe0\x91-\x05y<\x97!\x13\xea\u8b88\x89b\x8d\xd1w\u04bc(\x00\x00\u07d4}\x90\x1b(\xbf\u007f\x88\xefs\xd8\xf7<\u0297VI\x13\xea\x8a$\x893\xc5I\x901r\f\x00\x00\u07d4}\x98\x0fKVk\xb0EQ~L\x14\xc8wP\u0793FtK\x89Hz\x9a0E9D\x00\x00\xe0\x94}\x9cYc\x1e+\xa2\xe8\xe8(\x91\xf3\x97\x99\"\xaa\xa3\xb5g\xa1\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4}\x9d\"\x1a=\xf8\x9d\xdd{_a\xc1F\x8cg\x87\u05b33\xe6\x89\a{\"|\xd8;\xe8\x00\x00\u07d4}\xa7a4E\xa2\x12\x99\xaat\xf0\xadqC\x1e\xc4?\xbb\x1b\xe9\x89\x03\xaf\xb0\x87\xb8v\x90\x00\x00\u07d4}\xb4\xc7\u0577\x97\xe9)nc\x82\xf2\x03i=\xb4\tD\x9db\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4}\xb9\xea\xccR\xe4)\u0703\xb4a\xc5\xf4\xd8`\x10\xe58:(\x8965\u026d\xc5\u07a0\x00\x00\u07d4}\xd4m\xa6w\xe1a\x82^\x12\xe8\r\xc4F\xf5\x82v\xe1\x12|\x89,s\xc97t,P\x00\x00\u07d4}\xd8\u05e1\xa3O\xa1\xf8\xe7<\xcb\x00_\u00a0:\x15\xb8\"\x9c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4}\xddW\x16\\\x87\xa2p\u007f\x02]\xcf\xc2P\x8c\t\x83GY\xbc\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4}\xe4B\xc8#\x86\x15M.\x99<\xbd\x12\x80\xbb|\xa6\xb1*\u0689\xd8\xf2\xe8$~\xc9H\x00\x00\u07d4}\xe7\xfeA\x9c\xc6\x1f\x91\xf4\b\xd24\u0300\xd5\xca=\x05M\x99\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4}\xec\u664a\xe1\x90\r\xd3w\f\xf4\xb98\x12\xba\xd8O\x03\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4}\xfc4-\xff\xcfE\xdf\xeet\xf8L\t\x959{\u04661r\x89\r\x8drkqw\xa8\x00\x00\u07d4}\xfd)b\xb5u\xbc\xbe\xee\x97\xf4\x91B\xd6<0\xab\x00\x9ff\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4~\x1e)r\x1dl\xb9\x10W\xf6\xc4\x04-\x8a\v\xbcdJ\xfes\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4~#ff\xb2\xd0nc\xeaN*\xb8CW\xe2\xdf\xc9w\xe5\x0e\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94~$\xd9\xe2,\xe1\xda<\xe1\x9f!\x9c\xce\xe5#7hs\xf3g\x8a\x01?\xd9\a\x9c\xaa`\xff\x00\x00\u07d4~$\xfb\xda\u0490\x17^\xb2\xdfm\x18\n\x19\xb9\xa9\xf4\x13p\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94~&\x8f\x13\x1d\xdfh|\xc3%\xc4\x12\xf7\x8b\xa9a ^\x91\x12\x8a\x03cd\xee}0\x1b<\x00\x00\u07d4~))\x008I5Y\x19N\x94mNF\v\x96\xfc8\xa1V\x89\x10\xc1<Rwc\x88\x00\x00\u07d4~+\xa8m\xa5.x]\x86%3O3\x97\xba\x1cK\xf2\xe8\u0449\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4~?c\xe11)\xa2!\xba\x1a\xb0c&4,\u064bQ&\xae\x89V\xa0&Y\xa5#4\x00\x00\u07d4~Gc~\x97\xc1F\"\x88+\xe0W\xbe\xa2)8o@R\xe5\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4~N\x94\tpA!\xd1\xd7y\x97\x02o\xf0n\xa9\xb1\x9a\x8b\x90\x89\x8d\x16T\x9e\u054f\xa4\x00\x00\u07d4~Y\xdc`\xbe\x8b/\xc1\x9a\xbd\nW\x82\xc5,(@\v\u0397\x8965\u026d\xc5\u07a0\x00\x00\u07d4~[\x19\xae\x1b\xe9O\xf4\xde\xe65I*\x1b\x01-\x14\xdb\x02\x13\x89\x05k\xc7^-c\x10\x00\x00\u07d4~]\x99\x93\x10NL\xb5E\xe1y\xa2\xa3\xf9q\xf7D\xf9\x84\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4~q\x17\x1f)I\xfa\f:\xc2T%K\x1f\x04@\xe5\xe6\xa08\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4~|\x1e\x9aa\xa0\x8a\x83\x98H5\xc7\x0e\xc3\x1d4\xd3\xea\xa8\u007f\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4~\u007f\x18\xa0.\u032a]a\xab\x8f\xbf\x03\x03C\xc44\xa2^\xf7\x89\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\u07d4~\x81\xf6D\x9a\x037A\x91\xf3\xb7\xcb\x05\xd98\xb7.\t\r\xff\x89\x05k\xc7^-c\x10\x00\x00\u07d4~\x86I\xe6\x90\xfc\x8c\x1b\xfd\xa1\xb5\xe1\x86X\x1fd\x9bP\xfe3\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4~\x87\x86>\xc4:H\x1d\xf0M\x01wb\xed\xcb\\\xaab\x9bZ\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4~\x8f\x96\xcc)\xf5{\tu\x12\f\xb5\x93\xb7\u0743=`kS\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4~\x97*\x8a|*D\xc9;!Cl8\xd2\x1b\x92R\xc3E\xfe\x89a\t=|,m8\x00\x00\u07d4~\x99\u07fe\x98\x9d;\xa5)\u0457Q\xb7\xf41\u007f\x89S\xa3\xe2\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4~\xa0\xf9n\xe0\xa5s\xa30\xb5h\x97v\x1f=L\x010\xa8\xe3\x89Hz\x9a0E9D\x00\x00\u0794~\xa7\x91\xeb\xab\x04E\xa0\x0e\xfd\xfcNJ\x8e\x9a~ue\x13m\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4~\xab\xa05\xe2\xaf7\x93\xfdtgK\x10%@\xcf\x19\n\u0779\x89E\x02l\x83[`D\x00\x00\xe0\x94~\xb4\xb0\x18\\\x92\xb6C\x9a\b\xe72!h\xcb5<\x8awJ\x8a\x02'\x19l\xa0I\x83\xca\x00\x00\xe0\x94~\xbd\x95\xe9\xc4p\xf7(5\x83\xdcn\x9d,M\xce\v\ua3c4\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4~\u0425\xa8G\xbe\xf9\xa9\xda|\xba\x1dd\x11\xf5\xc3\x161&\x19\x89\x02(\xeb7\xe8u\x1d\x00\x00\u07d4~\xda\xfb\xa8\x98K\xafc\x1a\x82\vk\x92\xbb\xc2\xc56U\xf6\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4~\xdb\x02\xc6\x1a\"r\x87a\x1a\xd9Pici\xccNdzh\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4~\xe5\u0280]\xce#\xaf\x89\xc2\xd4D\xe7\xe4\af\xc5Lt\x04\x89\r\v\xd4\x12\xed\xbd\x82\x00\x00\xe0\x94~\xe6\x04\u01e9\xdc)\t\xce2\x1d\u6e72OWgWuU\x8a\x01+\xf9\u01d8\\\xf6-\x80\x00\u07d4~\xf1o\xd8\xd1[7\x8a\x0f\xba0k\x8d\x03\u0758\xfc\x92a\x9f\x89%\xf2s\x93=\xb5p\x00\x00\u07d4~\xf9\x8bR\xbe\xe9S\xbe\xf9\x92\xf3\x05\xfd\xa0'\xf8\x91\x1cXQ\x89\x1b\xe7\" i\x96\xbc\x80\x00\u07d4~\xfc\x90vj\x00\xbcR7,\xac\x97\xfa\xbd\x8a<\x83\x1f\x8e\u0349\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4~\xfe\xc0\xc6%<\xaf9\u007fq(|\x1c\a\xf6\xc9X+[\x86\x89\x1a,\xbc\xb8O0\u0540\x00\u07d4\u007f\x01\xdc|7G\xca`\x8f\x98=\xfc\x8c\x9b9\xe7U\xa3\xb9\x14\x89\v8l\xad_zZ\x00\x00\u07d4\u007f\x06b\xb4\x10)\x8c\x99\xf3\x11\u04e1EJ\x1e\xed\xba/\xeav\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u007f\x06\u021dY\x80\u007f\xa6\v\xc6\x016\xfc\xf8\x14\u02ef%C\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u007f\v\x90\xa1\xfd\u050f'\xb2h\xfe\xb3\x83\x82\xe5]\xdbP\xef\x0f\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\u007f\x0e\xc3\u06c0F\x92\xd4\xd1\xea2E6Z\xab\x05\x90\a[\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u007f\x0f\x04\xfc\xf3zS\xa4\xe2N\xden\x93\x10Nx\xbe\x1d<\x9e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007f\x13\xd7`I\x8dq\x93\xcahY\xbc\x95\xc9\x018d#\xd7l\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u007f\x15\n\xfb\x1aw\u00b4Y(\xc2h\xc1\u9f74d\x1dG\u0609lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\x16\x19\x98\x8f7\x15\xe9O\xf1\xd2S&-\xc5X\x1d\xb3\xde\x1c\x890\xca\x02O\x98{\x90\x00\x00\u07d4\u007f\x1c\x81\xee\x16\x97\xfc\x14K|\v\xe5I;V\x15\xae\u007f\xdd\u0289\x1b\x1d\xaba\u04ead\x00\x00\u07d4\u007f#\x82\xff\xd8\xf89VFy7\xf9\xbar7F#\xf1\x1b8\x89 \x86\xac5\x10R`\x00\x00\u07d4\u007f7\t9\x1f?\xbe\xba5\x92\xd1u\xc7@\xe8z\tT\x1d\x02\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\u007f8\x9c\x12\xf3\xc6\x16OdFVlwf\x95\x03\xc2y%'\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\u007f:\x1eE\xf6~\x92\u0200\xe5s\xb43y\xd7\x1e\xe0\x89\xdbT\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\u007f=r\x03\u0224G\xf7\xbf6\u060a\xe9\xb6\x06*^\xeex\xae\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u007fF\xbb%F\r\xd7\xda\xe4!\x1c\xa7\xf1Z\xd3\x12\xfc}\xc7\\\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\u007fI\xe7\xa4&\x98\x82\xbd\x87\"\u0526\xf5f4v)b@y\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u007fI\xf2\a&G\x1a\xc1\u01e8>\xf1\x06\xe9w\\\xebf%f\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\u007fK^'\x85x\xc0F\xcc\xea\xf6W0\xa0\xe0h2\x9e\u0576\x89e\xea=\xb7UF`\x00\x00\u07d4\u007fOY;a\x8c3\v\xa2\xc3\xd5\xf4\x1e\xce\xeb\x92\xe2~Bl\x89\x96n\xdcuk|\xfc\x00\x00\u07d4\u007fT\x14\x91\u04ac\x00\xd2a/\x94\xaa\u007f\v\xcb\x01FQ\xfb\u0509\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\u007fZ\xe0Z\xe0\xf8\xcb\xe5\xdf\xe7!\xf0D\u05e7\xbe\xf4\xc2y\x97\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u007f`:\xec\x17Y\xea_\a\xc7\xf8\xd4\x1a\x14(\xfb\xba\xf9\xe7b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u007falo\x00\x8a\u07e0\x82\xf3M\xa7\xd0e\x04`6\x80u\xfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u007fa\xfal\xf5\xf8\x98\xb4@\xda\u016b\xd8`\rmi\x1f\xde\xf9\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u007fe\\g\x89\xed\xdfE\\\xb4\xb8\x80\x99r\x0698\x9e\ubb0a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u007fk(\u0204!\xe4\x85~E\x92\x81\u05c4ai$\x89\xd3\xfb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007fn\xfboC\x18\x87m.\xe6$\xe2u\x95\xf4DF\xf6\x8e\x93\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u007fq\x92\xc0\xdf\x1c}\xb6\xd9\xede\xd7\x11\x84\xd8\xe4\x15Z\x17\xba\x89\x04Sr\x8d3\x94,\x00\x00\u07d4\u007fz:!\xb3\xf5\xa6]\x81\xe0\xfc\xb7\xd5-\xd0\n\x1a\xa3m\xba\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u007f\x8d\xbc\xe1\x80\xed\x9cV65\xaa\xd2\xd9{L\xbcB\x89\x06\u0649\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\u007f\x99=\xdb~\x02\u0082\xb8\x98\xf6\x15_h\x0e\xf5\xb9\xaf\xf9\a\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u007f\x9f\x9bV\xe4(\x9d\xfbX\xe7\x0f\xd5\xf1*\x97\xb5m5\u01a5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u007f\xa3~\xd6x\x87u\x1aG\x1f\x0e\xb3\x06\xbeD\xe0\xdb\xcd`\x89\x899vt\u007f\xe1\x1a\x10\x00\x00\u07d4\u007f\xaa0\xc3\x15\x19\xb5\x84\xe9rP\xed*<\xf38^\xd5\xfdP\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xcf[\xa6fo\x96lTH\xc1{\xf1\xcb\v\xbc\xd8\x01\x9b\x06\x89\x05k\xc3\u042e\xbeI\x80\x00\xe0\x94\u007f\xd6y\xe5\xfb\r\xa2\xa5\xd1\x16\x19M\xcbP\x83\x18\xed\u0140\xf3\x8a\x01c\x9eI\xbb\xa1b\x80\x00\x00\u07d4\u007f\u06e01\u01cf\x9c\tmb\xd0Z6\x9e\uac3c\xccU\u5257\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\u007f\xdb\u00e8D\xe4\r\x96\xb2\xf3\xa652.`e\xf4\xca\x0e\x84\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xdf\u020dx\xbf\x1b(Z\xc6O\x1a\xdb5\xdc\x11\xfc\xb09Q\x89|\x06\xfd\xa0/\xb06\x00\x00\u07d4\u007f\xea\x19b\xe3]b\x05\x97h\xc7I\xbe\u0756\u02b90\xd3x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u007f\xef\x8c8w\x9f\xb3\a\xeco\x04K\xeb\xe4\u007f<\xfa\xe7\x96\xf1\x89\t#@\xf8l\xf0\x9e\x80\x00\u07d4\u007f\xf0\xc6?p$\x1b\xec\xe1\x9bs~SA\xb1+\x10\x901\u0609\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\u007f\xfa\xbf\xbc9\f\xbeC\u0389\x18\x8f\bh\xb2}\xcb\x0f\f\xad\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\u007f\xfd\x02\xed7\fp`\xb2\xaeS\xc0x\xc8\x01!\x90\u07fbu\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u0794\x80\x02*\x12\a\xe9\x10\x91\x1f\xc9(I\xb0i\xab\f\xda\xd0C\u04c8\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x80\t\xa7\xcb\u0452\xb3\xae\u052d\xb9\x83\xd5(ER\xc1ltQ\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\x0e}c\x1cnW:\x903/\x17\xf7\x1f_\u045bR\x8c\xb9\x89\b=lz\xabc`\x00\x00\u07d4\x80\x15m\x10\ufa320\u0254\x10c\r7\xe2i\xd4\t<\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\x172\xa4\x81\u00c0\xe5~\xd6-l)\u0799\x8a\xf3\xfa;\x13\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x1de\xc5\x18\xb1\x1d\x0e?OG\x02!Ap\x13\xc8\xe5>\u0149\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80&CZ\xacr\x8dI{\x19\xb3\xe7\xe5|(\xc5c\x95O+\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x80-\xc3\xc4\xff-}\x92^\u215fJ\x06\u05fa`\xf10\x8c\x89\x05P\x94\f\x8f\xd3L\x00\x00\u07d4\x800\xb1\x11\u0198?\x04\x85\u076c\xa7b$\xc6\x18\x064x\x9f\x89\x04V9\x18$O@\x00\x00\u07d4\x805\xbc\xff\xae\xfd\xee\xea5\x83\fI}\x14(\x9d6 #\u0789\x10CV\x1a\x88)0\x00\x00\u07d4\x805\xfeNkj\xf2z\u44a5xQ^\x9d9\xfao\xa6[\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80C\xed\"\xf9\x97\u58a4\xc1n6D\x86\xaed\x97V\x92\u0109=I\x04\xff\xc9\x11.\x80\x00\u07d4\x80C\xfd\u043cL\x97=\x16c\xd5_\xc15P\x8e\xc5\xd4\xf4\xfa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80L\xa9IrcOc:Q\xf3V\v\x1d\x06\xc0\xb2\x93\xb3\xb1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x80R-\u07d4N\xc5.'\xd7$\xedL\x93\xe1\xf7\xbe`\x83\u0589\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80Y\x1aB\x17\x9f4\xe6M\x9d\xf7]\xcdF;(hoUt\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x80\\\xe5\x12\x97\xa0y;\x81 g\xf0\x17\xb3\xe7\xb2\u07db\xb1\xf9\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x80]\x84o\xb0\xbc\x02\xa73r&\u0585\xbe\x9e\xe7s\xb9\x19\x8a\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x80c7\x9a{\xf2\u02d2:\x84\xc5\t>h\xda\xc7\xf7T\x81\u0149\x11v\x10.n2\xdf\x00\x00\u07d4\x80hTX\x8e\xcc\xe5AI_\x81\u008a)\x03s\xdf\x02t\xb2\x89\x1f\x8c\xdf\\n\x8dX\x00\x00\u07d4\x80oD\xbd\xebh\x807\x01^\x84\xff!\x80I\xe3\x823*3\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\x80tF\x18\xde9jT1\x97\xeeH\x94\xab\xd0c\x98\xdd|'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80w\xc3\xe4\xc4EXn\tL\xe1\x02\x93\u007f\xa0[s{V\x8c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x90\u007fY1H\xb5|F\xc1w\xe2=%\xab\u012a\xe1\x83a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\x97s\x16\x94NYB\xe7\x9b\x0e:\xba\u04cd\xa7F\be\x19\x89\x02\x1auJm\xc5(\x00\x00\xe0\x94\x80\xa0\xf6\xcc\x18l\xf6 \x14\x00sn\x06Z9\x1fR\xa9\xdfJ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x80\xab\xecZ\xa3n\\\x9d\t\x8f\x1b\x94(\x81\xbdZ\xca\u0196=\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb2=8\v\x82\\F\xe098\x99\xa8UVF-\xa0\u1309lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xb4-\xe1p\xdb\xd7#\xf4T\xe8\x8fw\x16E-\x92\x98P\x92\x89\x10F#\xc0v-\xd1\x00\x00\u07d4\x80\xb7\x9f3\x83\x90\u047a\x1b77\xa2\x9a\x02W\xe5\xd9\x1e\a1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x80\xbf\x99^\u063a\x92p\x1d\x10\xfe\u011f\x9e}\x01M\xbe\xe0&\x89\x1f\x047\xca\x1a~\x12\x80\x00\u07d4\x80\xc0N\xfd1\x0fD\x04\x83\xc7?tK[\x9edY\x9c\xe3\xec\x89A\rXj \xa4\xc0\x00\x00\u07d4\x80\u00e9\xf6\x95\xb1m\xb1Yr\x86\u0473\xa8\xb7il9\xfa'\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x80\xc5>\xe7\xe35\u007f\x94\xce\rxh\x00\x9c \x8bJ\x13\x01%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x80\xcc!\xbd\x99\xf3\x90\x05\u014f\xe4\xa4H\x90\x92 !\x8ff\u02c966\xc9yd6t\x00\x00\u07d4\x80\xd5\xc4\fY\xc7\xf5N\xa3\xa5_\xcf\xd1uG\x1e\xa3P\x99\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x80\xda/\u0762\x9a\x9e'\xf9\xe1\x15\x97^i\xae\x9c\xfb\xf3\xf2~\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x80\xe7\xb3 R0\xa5f\xa1\xf0a\xd9\"\x81\x9b\xb4\xd4\u04a0\xe1\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x80\xea\x1a\xcc\x13n\xcaKh\xc8B\xa9Z\xdfk\u007f\xee~\xb8\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x80\xf0z\xc0\x9e{,<\n=\x1e\x94\x13\xa5D\xc7:A\xbe\u02c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81\r\xb2Vu\xf4^\xa4\xc7\xf3\x17\u007f7\xce)\xe2-g\x99\x9c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81\x13\x9b\xfd\u0326V\xc40 ?r\x95\x8cT;e\x80\xd4\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\x14a\xa2\xb0\u0290\xba\xda\xc0j\x9e\xa1nx{3\xb1\x96\u0309\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\x81\x16M\xeb\x10\x81J\xe0\x83\x91\xf3,\bf{bH\xc2}z\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\x81\x18i1\x18A7\xd1\x19*\u020c\xd3\xe1\xe5\xd0\xfd\xb8jt\x89\x9d5\x95\xab$8\xd0\x00\x00\u0794\x81*U\xc4<\xae\xdcYr\x187\x90\x00\xceQ\rT\x886\xfd\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x81.\xa7\xa3\xb2\xc8n\xed2\xffO,sQL\xc6;\xac\xfb\u038965\u026d\xc5\u07a0\x00\x00\u07d4\x814\xdd\x1c\x9d\xf0\xd6\u0225\x81$&\xbbU\xc7a\u0283\x1f\b\x89\x06\xa2\x16\v\xb5|\xcc\x00\x00\u07d4\x81A5\u068f\x98\x11\aW\x83\xbf\x1a\xb6pb\xaf\x8d>\x9f@\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x81I\x8c\xa0{\x0f/\x17\xe8\xbb\xc7\xe6\x1a\u007fJ\xe7\xbef\xb7\x8b\x89\x05\x81\xfb\xb5\xb3;\xb0\x00\x00\u07d4\x81Um\xb2sI\xab\x8b'\x00ID\xedP\xa4n\x94\x1a\x0f_\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x81U\xfalQ\xeb1\xd8\bA-t\x8a\xa0\x86\x10P\x18\x12/\x89e\xea=\xb7UF`\x00\x00\xe0\x94\x81V6\v\xbd7\ta\xce\xcakf\x91\xd7P\x06\xad L\xf2\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x81a\xd9@\xc3v\x01\x00\xb9\b\x05)\xf8\xa6\x03%\x03\x0fn\u0709\x10CV\x1a\x88)0\x00\x00\xe0\x94\x81d\xe7\x83\x14\xae\x16\xb2\x89&\xccU=,\xcb\x16\xf3V'\r\x8a\x01\xca\x13N\x95\xfb2\xc8\x00\x00\u07d4\x81e\u02b0\xea\xfbZ2\x8f\xc4\x1a\xc6M\xaeq[.\xef,e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81h\xed\xce\u007f)a\xcf)[\x9f\xcdZE\xc0l\xde\xdan\xf5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81m\x97r\xcf\x119\x91\x16\xcc\x1er\xc2lgt\xc9\xed\xd79\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x81s\xc85djg.\x01R\xbe\x10\xff\xe8Ab\xdd%nL\x89\x1a\xab\xdf!E\xb40\x00\x00\u07d4\x81t\x93\u035b\xc6#p*$\xa5o\x9f\x82\xe3\xfdH\xf3\xcd1\x89\x9eK#\xf1-L\xa0\x00\x00\u07d4\x81y\xc8\tp\x18,\u0177\xd8*M\xf0n\xa9M\xb6:%\xf3\x89'o%\x9d\xe6k\xf4\x00\x00\u07d4\x81z\xc3;\xd8\xf8GVsr\x95\x1fJ\x10\u05e9\x1c\xe3\xf40\x89\n\xd7\xc4\x06\xc6m\xc1\x80\x00\xe0\x94\x81\x8f\xfe'\x1f\u00d75e\xc3\x03\xf2\x13\xf6\xd2\u0689\x89~\xbd\x8a\x016\xe0SB\xfe\u1e40\x00\u07d4\x81\x97\x94\x81!s.c\xd9\xc1H\x19N\xca\xd4n0\xb7I\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\x9a\xf9\xa1\xc2s2\xb1\xc3i\xbb\xda\x1b=\xe1\xc6\xe93\xd6@\x89\x11\t\xe6T\xb9\x8fz\x00\x00\xe0\x94\x81\x9c\u06a506x\xef|\xecY\u050c\x82\x16:\xcc`\xb9R\x8a\x03\x13QT_y\x81l\x00\x00\u07d4\x81\x9e\xb4\x99\vZ\xbaUG\t=\xa1+k<\x10\x93\xdfmF\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xa8\x81\x96\xfa\xc5\xf2<>\x12\xa6\x9d\xecK\x88\x0e\xb7\xd9s\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x81\xbc\xcb\xff\x8fD4~\xb7\xfc\xa9['\xce|\x95$\x92\xaa\xad\x89\b@\xc1!e\xddx\x00\x00\u07d4\x81\xbdu\xab\xd8e\xe0\xc3\xf0J\vO\xdb\xcbt\xd3@\x82\xfb\xb7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x81\xc1\x8c*#\x8d\xdcL\xba#\n\a-\xd7\xdc\x10\x1eb\x02s\x89Hz\x9a0E9D\x00\x00\u07d4\x81\xc9\xe1\xae\xe2\xd36]S\xbc\xfd\u0356\xc7\xc58\xb0\xfd~\xec\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x81\u03edv\t\x13\xd3\xc3\"\xfc\xc7{I\u00ae9\a\xe7On\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x81\xd6\x19\xffW&\xf2@_\x12\x90Lr\xeb\x1e$\xa0\xaa\xeeO\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x81\xef\u25aev\xc8`\xd1\xc5\xfb\xd3=G\xe8\u0399\x96\xd1W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x81\xf8\xde,(=_\u052f\xbd\xa8]\xed\xf9v\x0e\xab\xbb\xb5r\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x82\f\x19)\x11\x96P[e\x05\x9d\x99\x14\xb7\t\v\xe1\u06c7\u0789\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x82\x1c\xb5\xcd\x05\xc7\uf41f\xe1\xbe`s=\x89c\xd7`\xdcA\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x82\x1dy\x8a\xf1\x99\x89\u00ee[\x84\xa7\xa7(<\xd7\xfd\xa1\xfa\xbe\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x82\x1e\xb9\t\x94\xa2\xfb\xf9K\xdc23\x91\x02\x96\xf7o\x9b\xf6\xe7\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x82$\x9f\xe7\x0fa\u01b1o\x19\xa3$\x84\x0f\xdc\x02\x021\xbb\x02\x8a\x02\x036\xb0\x8a\x93c[\x00\x00\u07d4\x82(\xeb\xc0\x87H\x0f\xd6EG\xca(\x1f^\xac\xe3\x04\x14S\xb9\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\x82)\u03b9\xf0\xd7\b9I\x8dD\xe6\xab\xed\x93\xc5\xca\x05\x9f]\x8a\x1a\x1c\x1b<\x98\x9a \x10\x00\x00\u07d4\x82.\xdf\xf66V:a\x06\xe5.\x9a%\x98\xf7\xe6\xd0\xef'\x82\x89\x01\xf4\xf9i=B\u04c0\x00\u07d4\x822\x19\xa2Yv\xbb*\xa4\xaf\x8b\xadA\xac5&\xb4\x936\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x822\xd1\xf9t.\u07cd\xd9'\xda5;*\xe7\xb4\xcb\xceu\x92\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x824\xf4c\u0444\x85P\x1f\x8f\x85\xac\xe4\x97,\x9bc-\xbc\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x827htg7\xcem\xa3\x12\xd5>TSN\x10o\x96|\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82;\xa7dr8\xd1\x13\xbc\xe9\x96JC\u0420\x98\x11\x8b\xfeM\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x82@t1(\x06\xdaGHCBf\xee\x00!@\u305a\u0089Q\xb1\u04c3\x92a\xac\x00\x00\u07d4\x82C\x8f\u04b3*\x9b\xddgKI\xd8\xcc_\xa2\xef\xf9x\x18G\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82HW(\xd0\xe2\x81V7X\xc7Z\xb2~\xd9\u80a0\x00-\x89\a\xf8\b\xe9)\x1el\x00\x00\u07d4\x82K<<D>\x19)]~\xf6\xfa\xa7\xf3t\xa4y\x84\x86\xa8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82Q5\x8c\xa4\xe0`\u0775Y\xcaX\xbc\v\u077e\xb4\a\x02\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82Q5\xb1\xa7\xfc\x16\x05aL\x8a\xa4\u042cm\xba\u040fH\x0e\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\x82S\t\xa7\xd4]\x18\x12\xf5\x1en\x8d\xf5\xa7\xb9ol\x90\x88\x87\x89\x804\xf7\u0671f\xd4\x00\x00\u07d4\x82Z\u007fN\x10\x94\x9c\xb6\xf8\x96Bh\xf1\xfa_W\xe7\x12\xb4\u0109\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x82a\xfa#\f\x90\x1dC\xffW\x9fG\x80\u04d9\xf3\x1e`v\xbc\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82b\x16\x9baXp\x13N\xb4\xacl_G\x1ck\xf2\xf7\x89\xfc\x89\x19\x12z\x13\x91\xea*\x00\x00\u07d4\x82c\xec\xe5\xd7\t\xe0\u05eeq\u0328h\xed7\xcd/\xef\x80{\x895\xab\x02\x8a\xc1T\xb8\x00\x00\xe0\x94\x82l\xe5y\x052\xe0T\x8ca\x02\xa3\r>\xac\x83k\xd68\x8f\x8a\x03\xcf\xc8.7\xe9\xa7@\x00\x00\u07d4\x82n\xb7\xcds\x19\xb8-\xd0z\x1f;@\x90q\xd9n9g\u007f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x82u1\xa6\u0141z\xe3_\x82\xb0\v\x97T\xfc\xf7LU\xe22\x89\xc3(\t>a\xee@\x00\x00\u0794\x82u\xcdhL6y\u0548}\x03fN3\x83E\xdc<\xdd\xe1\x88\xdbD\xe0I\xbb,\x00\x00\u07d4\x82\x84\x92;b\u62ff|+\x9f4\x14\xd1>\xf6\xc8\x12\xa9\x04\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\x82\x8b\xa6Q\u02d3\x0e\xd9xqV)\x9a=\xe4L\u040br\x12\x89Hz\x9a0E9D\x00\x00\u07d4\x82\xa1\\\xef\x1dl\x82`\xea\xf1Y\xea?\x01\x80\xd8g}\xce\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xa8\xb9kl\x9e\x13\xeb\xec\x1e\x9f\x18\xac\x02\xa6\x0e\xa8\x8aH\xff\x89lk\x8c@\x8es\xb3\x00\x00\u07d4\x82\xa8\u02ff\xdf\xf0+.8\xaeK\xbf\xca\x15\xf1\xf0\xe8;\x1a\xea\x89\x04\x9b\x99\x1c'\xefm\x80\x00\u07d4\x82\xe4F\x1e\xb9\xd8I\xf0\x04\x1c\x14\x04!\x9eBr\u0110\n\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x82\xe5w\xb5\x15\xcb+\b`\xaa\xfe\x1c\xe0\x9aY\xe0\x9f\xe7\xd0@\x89 \x86\xac5\x10R`\x00\x00\u07d4\x82\xea\x01\xe3\xbf.\x83\x83nqpN\"\xa2q\x93w\xef\xd9\u00c9\xa4\xccy\x95c\u00c0\x00\x00\u07d4\x82\xf2\xe9\x91\xfd2L_]\x17v\x8e\x9fa3]\xb61\x9dl\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x82\xf3\x9b'X\xaeB'{\x86\u059fu\xe6(\xd9X\xeb\u02b0\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\x82\xf8T\xc9\xc2\xf0\x87\xdf\xfa\x98Z\xc8 \x1ebl\xa5Fv\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\x82\xffqo\xdf\x03>\xc7\xe9B\xc9\t\u0643\x18g\xb8\xb6\xe2\xef\x89a\t=|,m8\x00\x00\u07d4\x83\b\xed\n\xf7\xf8\xa3\xc1u\x1f\xaf\xc8w\xb5\xa4*\xf7\xd3X\x82\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x83\x1cD\xb3\b@G\x18K*\xd2\x18h\x06@\x907P\xc4]\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x83!\x05\x83\xc1jN\x1e\x1d\xac\x84\xeb\xd3~=\x0f|W\ub909lk\x93[\x8b\xbd@\x00\x00\u07d4\x83,T\x17k\xdfC\xd2\u027c\u05f8\b\xb8\x95V\xb8\x9c\xbf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x833\x16\x98]Gt+\xfe\xd4\x10`J\x91\x95<\x05\xfb\x12\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x834vK{9zNW\x8fP6M`\xceD\x89\x9b\xff\x94\x89\x05\x03\xb2\x03\xe9\xfb\xa2\x00\x00\xe0\x94\x83;j\x8e\xc8\xda@\x81\x86\xac\x8a}*m\xd6\x15#\xe7\u0384\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x83=?\xaeT*\xd5\xf8\xb5\f\xe1\x9b\xde+\xecW\x91\x80\u020c\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x83=\xb4,\x14\x16<{\xe4\u02b8j\u0153\xe0bf\u0599\u054a$\xe4\r+iC\xef\x90\x00\x00\xe0\x94\x83V;\xc3d\ud060\xc6\xda;V\xffI\xbb\xf2g\x82z\x9c\x8a\x03\xab\x91\xd1{ \xdeP\x00\x00\u07d4\x83zd]\xc9\\IT\x9f\x89\x9cN\x8b\u03c7S$\xb2\xf5|\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\x83\x8b\xd5e\xf9\x9f\xdeH\x05?y\x17\xfe3<\xf8J\xd5H\xab\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x83\x90\x8a\xa7G\x8am\x1c\x9b\x9b\x02\x81\x14\x8f\x8f\x9f$+\x9f\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x83\x92\xe57vq5x\x01[\xffI@\xcfC\x84\x9d}\u02e1\x89\bM\xf05]V\x17\x00\x00\xe0\x94\x83\x97\xa1\xbcG\xac\xd6GA\x81Y\xb9\x9c\xeaW\xe1\xe6S-n\x8a\x01\xf1\x0f\xa8'\xb5P\xb4\x00\x00\u07d4\x83\x98\xe0~\xbc\xb4\xf7_\xf2\x11m\xe7|\x1c*\x99\xf3\x03\xa4\u03c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xa3\x14\x883\xd9dI\x84\xf7\xc4u\xa7\x85\a\x16\ufd00\xff\x89\xb8Pz\x82\a( \x00\x00\u07d4\x83\xa4\x02C\x8e\x05\x19w=TH2k\xfba\xf8\xb2\f\xf5-\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\x83\xa9;[\xa4\x1b\xf8\x87 \xe4\x15y\f\xdc\vg\xb4\xaf4\u0109\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x83\xc2=\x8aP!$\xee\x15\x0f\b\xd7\x1d\xc6rt\x10\xa0\xf9\x01\x8a\a3\x1f;\xfef\x1b\x18\x00\x00\u07d4\x83\u0217\xa8Ki^\xeb\xe4fy\xf7\xda\x19\xd7vb\x1c&\x94\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xd52\u04cdm\xee?`\xad\u018b\x93a3\u01e2\xa1\xb0\u0749\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x83\xdb\xf8\xa1(S\xb4\n\xc6\x19\x96\xf8\xbf\x1d\xc8\xfd\xba\xdd\xd3)\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x83\xdb\xfd\x8e\xda\x01\xd0\u078e\x15\x8b\x16\u0413_\xc28\n]\u01c9 \x86\xac5\x10R`\x00\x00\u07d4\x83\xe4\x80U2|(\xb5\x93o\xd9\xf4D~s\xbd\xb2\xdd3v\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\x83\xfeZ\x1b2\x8b\xaeD\a\x11\xbe\xafj\xad`&\xed\xa6\xd2 \x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x84\x00\x8ar\xf8\x03o?\xeb\xa5B\xe3Px\xc0W\xf3*\x88%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84\x0e\xc8>\xa96!\xf04\xe7\xbb7b\xbb\x8e)\xde\xd4\xc4y\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\x84\x11E\xb4H@\xc9F\xe2\x1d\xbc\x19\x02d\xb8\xe0\xd5\x02\x93i\x8a?\x87\bW\xa3\xe0\xe3\x80\x00\x00\u07d4\x84#!\a\x93+\x12\xe01\x86X5%\xce\x02:p>\xf8\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\x84$O\xc9ZiW\xed|\x15\x04\xe4\x9f0\xb8\xc3^\xcaKy\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x841'}{\xdd\x10E}\xc0\x17@\x8c\x8d\xbb\xbdAJ\x8d\xf3\x89\x02\"\xc8\xeb?\xf6d\x00\x00\u07d4\x847Z\xfb\xf5\x9b:\x1da\xa1\xbe2\xd0u\xe0\xe1ZO\xbc\xa5\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84;\xd3P/E\xf8\xbcM\xa3p\xb3#\xbd\xac?\xcf_\x19\xa6\x89P\x03\x9dc\xd1\x1c\x90\x00\x00\u07d4\x84P34c\rw\xf7AG\xf6\x8b.\bf\x13\xc8\xf1\xad\xe9\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x84R\x03u\x0fqH\xa9\xaa&)!\xe8mC\xbfd\x19t\xfd\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x84a\xec\u0126\xa4^\xb1\xa5\xb9G\xfb\x86\xb8\x80i\xb9\x1f\xcdo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84g^\x91wrmE\xea\xa4k9\x92\xa3@\xba\u007fq\f\x95\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x84hl{\xadv,T\xb6g\u055f\x90\x94<\xd1M\x11z&\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\x89\xf6\xad\x1d\x9a\x94\xa2\x97x\x91V\x89\x9d\xb6AT\xf1\u06f5\x89\x13t\a\xc0<\x8c&\x80\x00\u07d4\x84\x8c\x99Jy\x00?\xe7\xb7\xc2l\xc62\x12\xe1\xfc/\x9c\x19\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x8f\xbd)\xd6|\xf4\xa0\x13\xcb\x02\xa4\xb1v\xef$N\x9e\u6349\x01\x17*ck\xbd\xc2\x00\x00\u07d4\x84\x94\x9d\xbaU\x9ac\xbf\xc8E\xde\xd0n\x9f-\x9b\u007f\x11\xef$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\x9a\xb8\a\x90\xb2\x8f\xf1\xff\u05ba9N\xfctc\x10\\6\xf7\x89\x01\xe0+\xe4\xael\x84\x00\x00\u07d4\x84\x9b\x11oYc\x01\xc5\u063bb\xe0\xe9z\x82H\x12n9\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x84\xa7L\xee\xcf\xf6\\\xb9;/\x94\x9dw>\xf1\xad\u007f\xb4\xa2E\x89\x05\n\x9bDF\x85\xc7\x00\x00\u07d4\x84\xaa\xc7\xfa\x19\u007f\xf8\\0\xe0;zS\x82\xb9W\xf4\x1f:\xfb\x89\b\x8b#\xac\xff\u0650\x00\x00\u07d4\x84\xaf\x1b\x15sB\xd5Ch&\r\x17\x87b0\xa54\xb5K\x0e\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x84\xb0\xeek\xb87\u04e4\xc4\xc5\x01\x1c:\"\x8c\x0e\u06b4cJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb4\xb7Nf#\xba\x9d\x15\x83\xe0\u03feId?\x168AI\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x84\xb6\xb6\xad\xbe/[>-h,f\xaf\x1b\u0110S@\xc3\xed\x89!\x92\xf8\xd2\"\x15\x00\x80\x00\xe0\x94\x84\xb9\x1e.)\x02\xd0^+Y\x1bA\b;\u05fe\xb2\xd5,t\x8a\x02\x15\xe5\x12\x8bE\x04d\x80\x00\u07d4\x84\xbc\xbf\"\xc0\x96\a\xac\x844\x1d.\xdb\xc0;\xfb\x179\xd7D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x84\xbf\xce\xf0I\x1a\n\xe0iK7\u03ac\x02E\x84\xf2\xaa\x04g\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x84\xcb}\xa0P-\xf4\\\xf5a\x81{\xbd#b\xf4Q\xbe\x02\u0689Hz\x9a0E9D\x00\x00\u07d4\x84\xccxx\xda`_\xdb\x01\x9f\xab\x9bL\xcf\xc1Wp\x9c\u0765\x89Hy\x85\x13\xaf\x04\xc9\x00\x00\u07d4\x84\xdb\x14Y\xbb\x00\x81.\xa6~\xcb=\xc1\x89\xb7!\x87\xd9\xc5\x01\x89\b\x11\xb8\xfb\u0685\xab\x80\x00\u07d4\x84\u9516\x80\xbe\xcehA\xb9\xa7\xe5%\r\b\xac\xd8}\x16\u0349\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x84\xe9\u03c1f\xc3j\xbf\xa4\x90S\xb7\xa1\xad@6 &\x81\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x84\xec\x06\xf2G\x00\xfeBAL\xb9\x89|\x15L\x88\xde/a2\x89Hz\x9a0E9D\x00\x00\xe0\x94\x84\xf5\"\xf0R\x0e\xbaR\xdd\x18\xad!\xfaK\x82\x9f+\x89\u02d7\x8a\x01\fQ\x06\xd5\x13O\x13\x00\x00\u07d4\x85\v\x9d\xb1\x8f\xf8K\xf0\xc7\xdaI\xea7\x81\xd9 \x90\xad~d\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\x85\x10\xee\x93O\f\xbc\x90\x0e\x10\a\xeb8\xa2\x1e*Q\x01\xb8\xb2\x89\x05\xbf\v\xa6cOh\x00\x00\u07d4\x85\x16\xfc\xafw\u0213\x97\x0f\xcd\x1a\x95\x8b\xa9\xa0\x0eI\x04@\x19\x89\n\xa3\xeb\x16\x91\xbc\xe5\x80\x00\u07d4\x85\x1a\xa9\x1c\x82\xf4/\xad]\xd8\xe8\xbb^\xa6\x9c\x8f:Yw\u0449\b\x0eV\x1f%xy\x80\x00\u07d4\x85\x1c\rb\xbeF5\xd4w~\x805\xe3~K\xa8Q|a2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x85\x1d\u00ca\xdbE\x93r\x9av\xf3:\x86\x16\u06b6\xf5\xf5\x9aw\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x852I\b\x97\xbb\xb4\u038b\u007fk\x83~L\xba\x84\x8f\xbe\x99v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x85>j\xba\xf4Di\xc7/\x15\x1dN\"8\x19\xac\xedN7(\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85F\x91\xceqO2\\\xedU\xceY(\u039b\xa1/\xac\u0478\x89\xedp\xb5\xe9\xc3\xf2\xf0\x00\x00\u07d4\x85L\fF\x9c$k\x83\xb5\u0473\xec\xa4C\xb3\x9a\xf5\xee\x12\x8a\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x85]\x9a\xef,9\xc6#\r\t\u025e\xf6II\x89\xab\u61c5\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x85c\u0113a\xb6%\xe7hw\x1c\x96\x15\x1d\xbf\xbd\x1c\x90iv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85fa\t\x01\xaa\xce8\xb82D\xf3\xa9\xc810jg\xb9\u0709\xb0\x82\x13\xbc\xf8\xff\xe0\x00\x00\xe0\x94\x85j\xa2<\x82\xd7![\xec\x8dW\xf6\n\xd7^\xf1O\xa3_D\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x85nZ\xb3\xf6L\x9a\xb5k\x00\x93\x93\xb0\x16d\xfc\x03$\x05\x0e\x89a\t=|,m8\x00\x00\u07d4\x85n\xb2\x04$\x1a\x87\x83\x0f\xb2)\x03\x13C\xdc0\x85OX\x1a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x85s,\x06\\\xbdd\x11\x99A\xae\xd40\xacYg\vlQ\u0109'\xa5sb\xab\n\x0e\x80\x00\xe0\x94\x85x\xe1\x02\x12\xca\x14\xff\a2\xa8$\x1e7F}\xb8V2\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x85y\xda\xdf\x1a9Z4q\xe2\vov=\x9a\x0f\xf1\x9a?o\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x85\u007f\x10\v\x1aY0\"^\xfc~\x90 \u05c3'\xb4\x1c\x02\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x85\x94mV\xa4\xd3q\xa93hS\x96\x90\xb6\x0e\xc8%\x10tT\x89]\u0212\xaa\x111\xc8\x00\x00\xe0\x94\x85\x99\xcb\u0566\xa9\xdc\u0539f\xbe8}iw]\xa5\xe3<o\x8a\fQ\xf1\xb1\xd5&\"\x90\x00\x00\u07d4\x85\x9c`\f\xf1=\x1d\x02s\xd5\xd1\xda<\u05c9\u4549\x9f'\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\x85\xa2\xf6\xea\x94\xd0^\x8c\x1d\x9a\xe2\xf4\x91\x038\xa3X\xe9\x8d\xed\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x85\xb1o\v\x8b4\xdf\xf3\x80Oi\xe2\x16\x8aO{$\xd1\x04+\x89\x11/B<vF\xd4\x00\x00\xe0\x94\x85\xb2\x99\x8d\fs0,\xb2\xba\x13\xf4\x8913\x01\xe0S\xbe\x15\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x85\xbbQ\xbc;\xfe\x9a\x1b*/k\x1c\u0695\xbc\xa8\xb3\x8c\x8d^\x89\x11q-\xa0K\xa1\xef\x00\x00\u07d4\x85\xc8\xf3\xccz5O\xea\u025a^{\xfe|\u07e3Q\xcf\xe3U\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\x85\xca\x1er~\x9d\x1a\x87\x99\x1c\xc2\xc4\x18@\xeb\xb9\xed\xf2\x1d\x1b\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\x85\u028b\xc6\xda(\x03\xd0r_^\x1aEl\x89\xf9\xbcwN/\x89 \x86\xac5\x10R`\x00\x00\u07d4\x85\xd0\u0607T\xac\x84\xb8\xb2\x1b\xa9=\u04bf\xecrbo\xab\xa8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x85\xeb%kQ\xc8\x19\xd6\x0e\xa6\x1a\x82\xd1,\x93X\u055c\x1c\xae\x89\x18\xef\xc8J\xd0\u01f0\x00\x00\u07d4\x85\xf0\xe7\xc1\xe3\xaf\xf8\x05\xa6'\xa2\xaa\xf2\xcf\xf6\xb4\xc0\xdb\xe9\u02c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x86\x02l\xad?\xe4\xea\x1c\xe7\xfc\xa2`\xd3\xd4^\xb0\x9e\xa6\xa3d\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\x0f_\xfc\x10\xdev}\xed\x80\u007fq\xe8a\xd6G\xdf\xd2\x19\xb1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\x150c\xa1\xae\u007f\x02\xf1\xa8\x816\xd4\u059c|^>C'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86$_Yf\x91\t>\xce?=<\xa2&>\xac\xe8\x19A\u0649\n1\x06+\xee\xedp\x00\x00\u07d4\x86%i!\x1e\x8cc'\xb5A^:g\xe5s\x8b\x15\xba\xafn\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x86)}s\x0f\xe0\xf7\xa9\xee$\xe0\x8f\xb1\b{1\xad\xb3\x06\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86D\xcc(\x1b\xe32\xcc\xce\xd3m\xa4\x83\xfb*\aF\u067a.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86I\x9a\x12(\xff-~\xe3\au\x93dPo\x8e\x8c\x83\a\xa5\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x86K\xecPi\xf8U\xa4\xfdX\x92\xa6\xc4I\x1d\xb0|\x88\xff|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86W\n\xb2Y\u0271\xc3,\x97) /w\xf5\x90\xc0}\xd6\x12\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86c\xa2A\xa0\xa8\x9ep\xe1\x82\xc8E\xe2\x10\\\x8a\xd7&K\u03ca\x03#\xb1=\x83\x98\xf3#\x80\x00\u07d4\x86g\xfa\x11U\xfe\xd72\u03f8\u0725\xa0\xd7e\xce\r\a\x05\xed\x89\x04n\xc9e\u00d3\xb1\x00\x00\u07d4\x86h\xaf\x86\x8a\x1e\x98\x88_\x93\u007f&\x15\xde\xd6u\x18\x04\xeb-\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x86t\nFd\x8e\x84Z]\x96F\x1b\x18\t\x1f\xf5{\xe8\xa1o\x8a\x14\xc0\x974\x85\xbf9@\x00\x00\xe0\x94\x86~\xbaVt\x8aY\x045\r,\xa2\xa5\u039c\xa0\vg\n\x9b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x86\x80dt\xc3X\x04}\x94\x06\xe6\xa0\u007f@\x94[\xc82\x8eg\x8a\x01u.\xb0\xf7\x01=\x10\x00\x00\u07d4\x86\x88=T\xcd9\x15\xe5I\tU0\xf9\xab\x18\x05\xe8\xc5C-\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\x8c#\xbe\x874f\xd4\xc7L\"\n\x19\xb2E\xd1x~\x80\u007f\x89J\x13\xbb\xbd\x92\u020e\x80\x00\xe0\x94\x86\x92O\xb2\x11\xaa\xd2<\xf5\xce`\x0e\n\xae\x80c\x96D@\x87\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\x93\u9e3e\x94B^\xefyi\xbci\xf9\xd4/|\xadg\x1e\x8967\tlK\xcci\x00\x00\xe0\x94\x86\x9f\x1a\xa3\x0eDU\xbe\xb1\x82 \x91\xde\\\xad\xecy\xa8\xf9F\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x86\xa1\xea\xde\xeb0F\x13E\xd9\xefk\xd0R\x16\xfa$|\r\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xa5\xf8%\x9e\u0570\x9e\x18\x8c\xe3F\xee\x92\xd3J\xa5\u0753\xfa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xb7\xbdV<\uad86\xf9bD\xf9\xdd\xc0*\u05f0\xb1K\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x86\u008bVx\xaf7\xd7'\xec\x05\xe4Dw\x90\xf1_q\xf2\xea\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xc4\xce\x06\u066c\x18[\xb1H\xd9o{z\xbes\xf4A\x00m\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\xc8\xd0\u0642\xb59\xf4\x8f\x980\xf9\x89\x1f\x9d`z\x94&Y\x8a\x02\xce\xd3wa\x82O\xb0\x00\x00\u07d4\x86\xc94\xe3\x8eS\xbe;3\xf2t\xd0S\x9c\xfc\xa1Y\xa4\xd0\u04494\x95tD\xb8@\xe8\x00\x00\xe0\x94\x86\xca\x01E\x95~k\r\xfe6\x87_\xbez\r\xecU\xe1z(\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x86\u02af\xac\xf3*\xa01|\x03*\xc3k\xab\xed\x97G\x91\xdc\x03\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x86\u0377\xe5\x1a\xc4Gr\xbe6\x90\xf6\x1d\x0eYvn\x8b\xfc\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x86\xdfs\xbd7\u007f,\t\xdec\xc4]g\xf2\x83\xea\xef\xa0\xf4\xab\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x86\xe3\xfe\x86\xe9=\xa4\x86\xb1Bf\xea\xdf\x05l\xbf\xa4\xd9\x14C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x86\xe8g\x0e'Y\x8e\xa0\x9c8\x99\xabw\x11\u04f9\xfe\x90\x1c\x17\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x86\xefd&!\x19I\xcc7\xf4\xc7^xP6\x9d\f\xf5\xf4y\x8a\x02\xd6_2\xea\x04Z\xf6\x00\x00\u07d4\x86\xf0]\x19\x06>\x93i\xc6\x00N\xb3\xf1#\x94:|\xffN\xab\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x86\xf2>\x9c\n\xaf\u01cb\x9c@M\xcd`3\x9a\x92[\xff\xa2f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x86\xf4\xf4\n\u0644\xfb\xb8\t3\xaebn\x0eB\xf93?\xddA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x86\xf9\\[\x11\xa2\x93\x94\x0e5\xc0\xb8\x98\u0637_\b\xaa\xb0m\x8a\x06D\xe3\xe8u\xfc\xcft\x00\x00\u07d4\x86\xff\xf2 \xe5\x93\x05\xc0\x9fH8`\xd6\xf9N\x96\xfb\xe3/W\x89\x02S[j\xb4\xc0B\x00\x00\u07d4\x87\a\x96\xab\xc0\u06c4\xaf\x82\xdaR\xa0\xedhsM\xe7\xe66\xf5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x87\x0f\x15\xe5\u07cb\x0e\xab\xd0%iSz\x8e\xf9;Vx\\B\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\x87\x181`\xd1r\xd2\xe0\x84\xd3'\xb8k\xcb|\x1d\x8eg\x84\xef\x89\xd8\xd8X?\xa2\xd5/\x00\x00\xe0\x94\x87\x1b\x8a\x8bQ\u07a1\x98\x9aY!\xf1>\xc1\xa9U\xa5\x15\xadG\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x87%\xe8\xc7S\xb3\xac\xbf\u0725_<b\xdf\u1954T\x96\x8a\x8967\tlK\xcci\x00\x00\u07d4\x877\xda\xe6q\x82:\x8dY\x17\xe0\x15z\u039cCF\x8d\x94k\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x87;\u007fxm<\x99\xff\x01,J|\xae&w'\x02@\xb9\u0149]\u0212\xaa\x111\xc8\x00\x00\u07d4\x87<op\ufdb1\xd0\xf2\xbb\xc5~\xeb\xcdpa|l\xe6b\x896\xf0\xd5']\tW\x00\x00\u07d4\x87>I\x13\\3\x91\x99\x10`)\n\xa7\xf6\u0338\xf8Zx\u06c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x87Pa\xee\x12\xe8 \x04\x1a\x01\x94,\xb0\xe6[\xb4'\xb0\x00`\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x87XJ?a;\xd4\xfa\xc7L\x1ex\v\x86\xd6\xca\xeb\x89\f\xb2\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x87d\xd0'\"\x00\t\x96\xec\xd4u\xb43)\x8e\x9fT\v\x05\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x87l?!\x8bGv\xdf<\xa9\xdb\xfb'\r\xe1R\xd9N\xd2R\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x87u\xa6\x10\xc5\x02\xb9\xf1\xe6\xadL\xda\u06cc\xe2\x9b\xffu\xf6\xe4\x89 \x86\xac5\x10R`\x00\x00\u07d4\x87vN6w\xee\xf6\x04\xcb\u015a\xed$\xab\xdcVk\t\xfc%\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\x87\x87\xd1&w\xa5\xec)\x1eW\xe3\x1f\xfb\xfa\xd1\x05\xc32K\x87\x8a\x02\xa2N\xb52\b\xf3\x12\x80\x00\u07d4\x87\x94\xbfG\xd5E@\xec\xe5\xc7\"7\xa1\xff\xb5\x11\u0777Gb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x87\xa5>\xa3\x9fY\xa3[\xad\xa85%!dU\x94\xa1\xa7\x14\u02c9g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x87\xa7\xc5\b\xefqX-\u0665Cr\xf8\x9c\xb0\x1f%/\xb1\x80\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x87\xaf%\xd3\xf6\xf8\xee\xa1S\x13\xd5\xfeEW\xe8\x10\xc5$\xc0\x83\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x87\xb1\x0f\x9c(\x00\x98\x17\x9a+v\xe9\u0390\xbea\xfc\x84M\r\x89Hz\x9a0E9D\x00\x00\u07d4\x87\xbf|\xd5\u0629)\xe1\u01c5\xf9\xe5D\x91\x06\xac#$c\u0249\x047\xb1\x1f\xccEd\x00\x00\u07d4\x87\u0118\x17\t4\xb8#=\x1a\xd1\xe7i1}\\G_/@\x897\b\xba\xed=h\x90\x00\x00\u07d4\x87\xcf6\xad\x03\xc9\xea\xe9\x05:\xbbRB\u0791\x17\xbb\x0f*\v\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x87\u05ec\x06S\xcc\xc6z\xa9\xc3F\x9e\xefCR\x19?}\xbb\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\x87\xe3\x06+#!\xe9\u07f0\x87\\\u311c\x9b.5\"\xd5\n\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x87\xe6\x03N\xcf#\xf8\xb5c\x9d_\x0e\xa7\n\"S\x8a\x92\x04#\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\x87\xefm\x8bj|\xbf\x9b\\\x8c\x97\xf6~\xe2\xad\u00a7;?w\x89\n\xdd\x1b\xd2<<H\x00\x00\u07d4\x87\xfb&\xc3\x1eHdMi14 \\\xaeC\xb2\x1f\x18aK\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x87\xfcF5&9D\xce\x14\xa4lu\xfaJ\x82\x1f9\xce\u007fr\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x87\xfc\xbe|A\x93\xff\xcb\b\x147y\u027e\xc8?\xe7\xfd\xa9\xfc\x89\x05o\x98]8dK\x80\x00\xe0\x94\x88\x01]r\x03\xc5\xe0\"J\xed\xa2\x86\xed\x12\xf1\xa5\x1bx\x933\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\x88\x10l'\xd2\vt\xb4\xb9\x8c\xa6+#+\xd5\xc9t\x11\x17\x1f\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x88\x120\x04|!\x1d-[\x00\xd8\xdeLQ9\xde^2'\u01ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x88*\xa7\x98\xbfA\xdf\x17\x9f\x85R\x010\xf1\\\xcd\xf5\x9b^X\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x88+\u04e2\xe9\xd7A\x10\xb2Ia\xc57w\xf2/\x1fF\xdc]\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\xe0\x94\x88,\x8f\x81\x87,y\xfe\xd5!\xcb_\x95\r\x8b\x03#\"\xeai\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x88/up\x83\x86e<\x80\x17\x1d\x06c\xbf\xe3\v\x01~\u042d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x884I\tdLz\u0513\x0f\xd8s\xca\x1c\r\xa2\xd44\xc0\u007f\x89\a's\x9f\xcb\x00M\x00\x00\u07d4\x884\xb2E4q\xf3$\xfb&\xbe[%\x16k[W&\x02]\x89\x1f\x0f\xf8\xf0\x1d\xaa\xd4\x00\x00\u07d4\x88:x\xae\xab\xaaP\xd8\xdd\xd8W\v\xcd4&_\x14\xb1\x93c\x89\xd2U\"\xfd\xa3y\xa1\x80\x00\u07d4\x88E\xe9\xf9\x0e\x963k\xac<ak\xe9\u0604\x02h>\x00L\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x88F\x92\x8dh2\x89\xa2\xd1\x1d\xf8\xdbz\x94t\x98\x8e\xf0\x13H\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x88I\x80\xebEe\xc1\x04\x83\x17\xa8\xf4\u007f\u06f4a\x96[\u4049\xd8\xd6\x11\x9a\x81F\x05\x00\x00\xe0\x94\x88Jz9\u0411n\x05\xf1\xc2B\xdfU`\u007f7\u07cc_\u068a\x04\xf4\x84<\x15|\x8c\xa0\x00\x00\u07d4\x88T\x93\xbd\xa3j\x042\x97eF\xc1\xdd\xceq\xc3\xf4W\x00!\x89\v\xbfQ\r\xdf\xcb&\x00\x00\xe0\x94\x88`\x9e\nF[n\x99\xfc\xe9\a\x16mW\xe9\xda\b\x14\xf5\u020a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88m\n\x9e\x17\xc9\xc0\x95\xaf.\xa25\x8b\x89\xecpR\x12\ue509\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\x88y~Xg^\xd5\xccL\x19\x98\a\x83\xdb\xd0\xc9V\bQS\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88|\xacA\xcdpo3E\xf2\xd3J\xc3N\x01u*nY\t\x89 F\\\ue7617\x00\x00\u07d4\x88\x88\x8aW\xbd\x96\x87\xcb\xf9P\xae\xea\u03d7@\xdc\xc4\xd1\xefY\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u0794\x88\x89D\x83\x16\xcc\xf1N\xd8m\xf8\xe2\xf4x\xdcc\xc43\x83@\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x88\x8c\x16\x14I3\x19|\xac&PM\xd7n\x06\xfdf\x00\u01c9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x88\x8e\x94\x91p\x83\xd1R +S\x1699\x86\x9d'\x11u\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\x90\x87\xf6o\xf2\x84\xf8\xb5\xef\xbd)I;pg3\xab\x14G\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\x88\x95\xebrb&\xed\xc3\xf7\x8c\u01a5\x15\a{2\x96\xfd\xb9^\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x88\x97Z_\x1e\xf2R\x8c0\v\x83\xc0\xc6\a\xb8\xe8}\u0593\x15\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\u07d4\x88\x9d\xa4\x0f\xb1\xb6\x0f\x9e\xa9\xbdzE>XL\xf7\xb1\xb4\xd9\xf7\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x88\x9d\xa6b\xebJ\n*\x06\x9d+\xc2K\x05\xb4\xee.\x92\xc4\x1b\x89Z,\x8cTV\xc9\xf2\x80\x00\u07d4\x88\xa1\"\xa28,R91\xfbQ\xa0\u032d;\xeb[rY\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xa2\x15D0\xc0\xe4\x11G\xd3\xc1\xfe\u3cf0\x06\xf8Q\xed\xbd\x8965f3\xeb\xd8\xea\x00\x00\u07d4\x88\xb2\x17\u0337\x86\xa2T\xcfM\xc5\u007f]\x9a\xc3\xc4U\xa3\x04\x83\x892$\xf4'#\xd4T\x00\x00\xe0\x94\x88\xbcC\x01.\xdb\x0e\xa9\xf0b\xacCxC%\n9\xb7\x8f\xbb\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x88\xc2Qj|\xdb\t\xa6'mr\x97\xd3\x0fZM\xb1\xe8K\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x88\xc3ad\rki7;\b\x1c\xe0\xc43\xbdY\x02\x87\xd5\xec\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x88\xd5A\xc8@\xceC\xce\xfb\xafm\x19\xafk\x98Y\xb5s\xc1E\x89\t79SM(h\x00\x00\u07d4\x88\xde\x13\xb0\x991\x87|\x91\rY1e\xc3d\u0221d\x1b\u04c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x88\xde\u017d?N\xba-\x18\xb8\xaa\xce\xfa{r\x15H\xc3\x19\xba\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x88\xe6\xf9\xb2G\xf9\x88\xf6\xc0\xfc\x14\xc5o\x1d\xe5>\u019dC\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x88\xee\u007f\x0e\xfc\x8fw\x8ckh~\xc3+\xe9\xe7\xd6\xf0 \xb6t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x88\xf1\x04_\x19\xf2\xd3\x19\x18\x16\xb1\xdf\x18\xbbn\x145\xad\x1b8\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x89\x00\x9e<d\x88\xbd^W\r\x1d\xa3N\xab\xe2\x8e\xd0$\xde\x1b\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x89\x05D0\xdc\xdc(\xac\x15\xfac^\xf8|\x10^`+\xf7\f\x89\x05\xda\xcd\x13\u029e0\x00\x00\xe0\x94\x89\bv\f\u04db\x9c\x1e\x81\x84\xe6\xa7R\ue20e?\vpE\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x89\x0f\xe1\x1f<$\u06c72\xd6\xc2\xe7r\xe2)|~e\xf19\x8a\rV'\x13}\xa8\xb5\x90\x00\x00\u07d4\x89\x14\xa6\x80\xa5\xae\xc5\"mK\xaa\xec.UR\xb4M\xd7\xc8t\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\x89\x1c\xb8#\x8c\x88\xe9:\x1b\xcfa\xdbI\xbd\x82\xb4z\u007fO\x84\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\x89%\xdaEI\xe1QU\xe5zb\x85\"\u03a9\xdd\xdfb}\x81\x8966\xc2^f\xec\xe7\x00\x00\u07d4\x890\x17\xff\x1a\xda\u0519\xaa\x06T\x01\xb4#l\xe6\xe9+bZ\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x893I\x17`\xc8\xf0\xb4\u07cc\xaa\u01ce\xd85\xca\xee!\x04m\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x896\bu\x1dh\xd0F\xe8X\x02\x92fs\xcd\xf2\xf5\u007f|\xb8\x89\x01\x11du\x9f\xfb2\x00\x00\u0794\x898\u0474\xda\xeeU\xa5Ms\x8c\xf1~Dw\xf6yNF\xf7\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x89:l.\xb8\xb4\n\xb0\x96\xb4\xf6~t\xa8\x97\xb8@tn\x86\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x89<\xdd\xdfSw\xf3\xc7Q\xbf.T\x11 \x04ZG\u02e1\x01\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x89V\x13#o5\x84!j\xd7\\]>\a\xe3\xfahc\xa7x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89Wr~r\xcfb\x90 \xf4\xe0^\xdfy\x9a\xa7E\x80b\u0409wC\"\x17\xe6\x83`\x00\x00\u07d4\x89]iN\x88\v\x13\xcc\u0404\x8a\x86\xc5\xceA\x1f\x88Gk\xbf\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x89^\xc5TVD\u0dc30\xff\xfa\xb8\xdd\xea\xc9\xe83\x15l\x89 \x86\xac5\x10R`\x00\x00\u07d4\x89`\tRj,{\f\t\xa6\xf6:\x80\xbd\U0009d707\u079c\x89\xbb\xb8k\x82#\xed\xeb\x00\x00\u07d4\x89g\u05f9\xbd\xb7\xb4\xae\xd2.e\xa1]\xc8\x03\xcbz!?\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x89n3\\\xa4z\xf5yb\xfa\x0fM\xbf>E\xe6\x88\u02e5\x84\x89J/\xc0\xab`R\x12\x00\x00\u07d4\x89s\xae\xfd^\xfa\xee\x96\t]\x9e(\x8fj\x04l\x977KC\x89\a\xa4\u0120\xf32\x14\x00\x00\u07d4\x89\x8cr\xddseX\xef\x9eK\xe9\xfd\xc3O\xefT\xd7\xfc~\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\x9b<$\x9f\fK\x81\xdfu\xd2\x12\x00M=m\x95/\xd2#\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x89\xab\x13\xee&mw\x9c5\xe8\xbb\x04\u034a\x90\xcc!\x03\xa9[\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x89\xc43\xd6\x01\xfa\xd7\x14\xdaci0\x8f\xd2l\x1d\u0254+\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x89\xd7[\x8e\b1\xe4o\x80\xbc\x17A\x88\x18N\x00o\xde\x0e\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x89\u3d5a\x15\x86G7\u0513\xc1\xd2<\xc5=\xbf\x8d\xcb\x13b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x89\xfc\x8eM8k\r\v\xb4\xa7\a\xed\xf3\xbdV\r\xf1\xad\x8fN\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\x89\xfe\xe3\r\x17(\xd9l\xec\xc1\u06b3\xda.w\x1a\xfb\u03eaA\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8a\x1c\u016c\x11\x1cI\xbf\xcf\xd8H\xf3}\xd7h\xaae\u0208\x02\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x8a \xe5\xb5\xce\xe7\xcd\x1fU\x15\xba\xce;\xf4\xf7\u007f\xfd\xe5\xcc\a\x89\x04V9\x18$O@\x00\x00\xe0\x94\x8a!}\xb3\x8b\xc3_!_\xd9)\x06\xbeBCo\xe7\xe6\xed\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x8a$:\n\x9f\xeaI\xb89TwE\xff-\x11\xaf?K\x05\"\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x8a$}\x18e\x10\x80\x9fq\xcf\xfcEYG\x1c9\x10\x85\x81!\x89a\t=|,m8\x00\x00\u07d4\x8a4p(-^**\xef\u05e7P\x94\xc8\"\xc4\xf5\xae\uf289\r(\xbc`dx\xa5\x80\x00\u07d4\x8a6\x86\x9a\xd4x\x99|\xbfm\x89$\xd2\n<\x80\x18\xe9\x85[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8aC\x14\xfba\u0353\x8f\xc3>\x15\xe8\x16\xb1\x13\U000ac267\xfb\x89\x17vNz\xede\x10\x00\x00\u07d4\x8aOJ\u007fR\xa3U\xba\x10_\xca r\xd3\x06_\xc8\xf7\x94K\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8aX1(,\xe1Jezs\r\xc1\x88&\xf7\xf9\xb9\x9d\xb9h\x89\uaf8a[A\xc16\x00\x00\u07d4\x8a_\xb7W\x93\xd0C\xf1\xbc\xd48\x85\xe07\xbd0\xa5(\xc9'\x89\x13Snm.\x9a\xc2\x00\x00\u07d4\x8af\xab\xbc-0\xce!\xa83\xb0\u06ceV\x1dQ\x05\xe0\xa7,\x89%\xf1\xde\\v\xac\xdf\x00\x00\u07d4\x8atl]g\x06G\x11\xbf\xcah[\x95\xa4\xfe)\x1a'\x02\x8e\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8ax\n\xb8z\x91E\xfe\x10\xed`\xfaGjt\n\xf4\u02b1\u0489\x12\x1b.^ddx\x00\x00\u07d4\x8az\x06\xbe\x19\x9a:X\x01\x9d\x84j\xc9\xcb\xd4\xd9]\xd7W\u0789\xa2\xa4#\x94BV\xf4\x00\x00\u07d4\x8a\x81\x01\x14\xb2\x02]\xb9\xfb\xb5\x00\x99\xa6\xe0\u02de.\xfak\u0709g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8a\x86\xe4\xa5\x1c\x01;\x1f\xb4\xc7k\xcf0f|x\xd5.\xed\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\x9e\u029cZ\xba\x8e\x13\x9f\x80\x03\xed\xf1\x16:\xfbp\xaa:\xa9\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\x8a\xb89\xae\xaf*\xd3|\xb7\x8b\xac\xbb\xb63\xbc\xc5\xc0\x99\xdcF\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\u021b\u06780\x1ek\x06w\xfa%\xfc\xf0\xf5\x8f\f\u01f6\x11\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x8a\xdcS\xef\x8c\x18\xed0Qx]\x88\xe9\x96\xf3\xe4\xb2\x0e\xcdQ\x8a\b\xe4\xd3\x16\x82v\x86@\x00\x00\u07d4\x8a\xe6\xf8\vp\xe1\xf2<\x91\xfb\u0569f\xb0\xe4\x99\xd9]\xf82\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x8a\xe9\uf30a\x8a\u07e6\xaby\x8a\xb2\xcd\xc4\x05\b*\x1b\xbbp\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8a\xf6&\xa5\xf3'\xd7Pe\x89\xee\xb7\x01\x0f\xf9\xc9D` \u0489K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\x8b\x01\xda4\xd4p\xc1\xd1\x15\xac\xf4\xd8\x11<M\u0628\xc38\xe4\x8a\x05W-\xce\xfa\xb6\x97\x90\x00\x00\u07d4\x8b\a\xd0PuM\u027a#\r\xb0\x1c1\n\xfd\xb59Z\xa1\xb3\x89\x06f\xb0nb\xa6 \x00\x00\u07d4\x8b \xad;\x94em\xbd\xc0\xdd!\xa3\x93\u0627\xd9\xe0!8\u02c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x8b'9\"\x06\xb9X\xcd7]~\xf8\xaf,\xf8\xef\x05\x98\xc0\xbc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x8b0\xc0@\x98\u05e7\xe6B\f5~\xa7\xbf\xa4\x9b\xac\x9a\x8a\x18\x8a\x01\xb1\xb1\x13\xf9\x1f\xb0\x14\x00\x00\u07d4\x8b3\x84\x11\xf2l\xcf7e\x8c\xc7U!\xd7v)\t\x9eF}\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8b6\"LsV\xe7Q\xf0\xc0f\xc3^;D\x86\x03d\xbf\u00896'\xba\u01e3\xd9'\x80\x00\xe0\x94\x8b6\x96\xf3\xc6\r\xe3$2\xa2\xe4\u00d5\xef\x03\x03\xb7\xe8\x1eu\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x8b9?\xb0\x81>\xe1\x01\xdb\x1e\x14\xec\xc7\xd3\"\xc7+\x8c\x04s\x89\x18\xb2j1>\x8a\xe9\x00\x00\xe0\x94\x8bH\xe1\x9d9\xdd5\xb6nn\x1b\xb6\xb9\xc6W\xcb,\xf5\x9d\x04\x8a\x03\xc7U\xac\x9c\x02J\x01\x80\x00\xe0\x94\x8bP^(q\xf7\u07b7\xa68\x95 \x8e\x82'\u072a\x1b\xff\x05\x8a\f\xf6\x8e\xfc0\x8dy\xbc\x00\x00\u07d4\x8bW\xb2\xbc\x83\u030dM\xe31 N\x89?/;\x1d\xb1\a\x9a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8b\\\x91K\x12\x8b\xf1i\\\b\x89#\xfaF~y\x11\xf3Q\xfa\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\xe0\x94\x8b_)\xcc/\xaa&,\xde\xf3\x0e\xf5T\xf5\x0e\xb4\x88\x14n\xac\x8a\x01;hp\\\x97 \x81\x00\x00\u07d4\x8bpV\xf6\xab\xf3\xb1\x18\xd0&\xe9D\xd5\xc0sC<\xa4Q\u05c965\xc6 G9\u0640\x00\u07d4\x8bqE\"\xfa(9b\x04p\xed\xcf\fD\x01\xb7\x13f=\xf1\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8bt\xa7\xcb\x1b\xb8\u014f\xce&tf\xa3\x03X\xad\xafR\u007fa\x8a\x02\xe2WxN%\xb4P\x00\x00\u07d4\x8b~\x9fo\x05\xf7\xe3dv\xa1n>q\x00\xc9\x03\x1c\xf4\x04\xaf\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8b\x81\x15ni\x869\x94<\x01\xa7Rr\xad=5\x85\x1a\xb2\x82\x89\x12\xb3\x16_e\xd3\xe5\x00\x00\u07d4\x8b\x95w\x92\x00S\xb1\xa0\x01\x890M\x88\x80\x10\xd9\xef,\xb4\xbf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8b\x98A\x86.w\xfb\xbe\x91\x94p\x93U\x83\xa9<\xf0'\xe4P\x89llS4B\u007f\x1f\x00\x00\u07d4\x8b\x99}\xbc\a\x8a\xd0)a5]\xa0\xa1Y\xf2\x92~\xd4=d\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8b\x9f\xda}\x98\x1f\xe9\xd6B\x87\xf8\\\x94\xd8?\x90t\x84\x9f\u030a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x8b\xb0!/2\x95\xe0)\u02b1\xd9a\xb0A3\xa1\x80\x9e{\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8b\xbe\xac\xfc)\xcf\xe94\x02\xdb<A\u065a\xb7Yf.s\xec\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x8b\xc1\xff\x87\x14\x82\x8b\xf2\x86\xff~\x8aw\t\x10eH\xed\x1b\x18\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x8b\u0436ZP\xef\\\xef\x84\xfe\xc4 \xbe{\x89\xed\x14p\u03b9\x8a\x02\x8aw\x93n\x92\xc8\x1c\x00\x00\u07d4\x8b\u05b1\xc6\xd7M\x01\r\x10\b\u06e6\xef\x83]D0\xb3\\2\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x8b\xd8\xd4\xc4\xe9C\xf6\xc8\a9!\xdc\x17\xe3\xe8\u05e0v\x16'\x89\x9f\x04!\x9d\x8d4\x95\x00\x00\u07d4\x8b\xdf\xdal!W \xed\xa2\x13o\x91\x05#!\xafN\x93l\x1f\x8965\xe6\x19\xbb\x04\xd4\x00\x00\u07d4\x8b\xea@7\x93G\xa5\u0211\u055acc1V@\xf5\xa7\xe0z\x89lkv\uf597\f\x00\x00\xe0\x94\x8b\xf0+\xd7Hi\x0e\x1f\xd1\xc7m'\b3\x04\x8bf\xb2_\u04ca\x02\u007f\xad\xe5h\xeb\xa9`\x00\x00\u07d4\x8b\xf2\x97\xf8\xf4SR>\xd6j\x1a\xcbvv\x85c7\xb9;\xf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8b\xf3s\xd0v\x81L\xbcW\xe1\xc6\xd1j\x82\u017e\x13\xc7=7\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x8c\x10#\xfd\xe1WM\xb8\xbbT\xf1s\x96p\x15|\xa4}\xa6R\x8a\x01y\u03da\u00e1\xb1w\x00\x00\u07d4\x8c\x1f\xbe_\n\xea5\x9cZ\xa1\xfa\b\u0209T\x12\u028e\x05\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\"B`U\xb7o\x11\xf0\xa2\xde\x1a\u007f\x81\x9aa\x96\x85\xfe`\x89kV\x05\x15\x82\xa9p\x00\x00\u07d4\x8c+}\x8b`\x8d(\xb7\u007f\\\xaa\x9c\xd6E$*\x82>L\u0649b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\x8c/\xbe\ue3ac\xc5\xc5\xd7|\x16\xab\xd4b\ue701E\xf3K\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8c:\x9e\xe7\x1fr\x9f#l\xba8g\xb4\u05dd\x8c\xee\xe2]\xbc\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8cP\xaa*\x92\x12\xbc\xdeVA\x8a\xe2a\xf0\xb3^z\x9d\xbb\x82\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8cT\xc7\xf8\xb9\x89nu\xd7\xd5\xf5\xc7`%\x86\x99\x95qB\xad\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x8c]\x16\xede\xe3\xed~\x8b\x96\u0297+\xc8as\xe3P\v\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8cj\xa8\x82\xee2,\xa8HW\x8c\x06\xcb\x0f\xa9\x11\xd3`\x83\x05\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\x8cj\xe7\xa0Z\x1d\xe5u\x82\xae'h Bv\xc0\xffG\xed\x03\x8a,\v\xb3\xdd0\xc4\xe2\x00\x00\x00\u07d4\x8co\x9fN[z\xe2v\xbfXI{\u05ff*}%$_d\x89\x93\xfe\\W\xd7\x10h\x00\x00\u07d4\x8cu\x95n\x8f\xedP\xf5\xa7\xdd|\xfd'\xda \x0fgF\xae\xa6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c|\xb4\xe4\x8b%\x03\x1a\xa1\xc4\xf9)%\xd61\xa8\xc3\xed\xc7a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\u007f\xa5\xca\xe8/\xed\xb6\x9a\xb1\x89\xd3\xff'\xae \x92\x93\xfb\x93\x89\x15\xaf\x88\r\x8c\u06c3\x00\x00\xe0\x94\x8c\x81A\x0e\xa85L\xc5\xc6\\A\xbe\x8b\xd5\xdes<\v\x11\x1d\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\x8c\x83\xd4$\xa3\xcf$\xd5\x1f\x01\x92=\xd5J\x18\u05b6\xfe\xde{\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8c\x90\n\x826\xb0\x8c+e@]9\xd7_ \x06*ua\xfd\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\x8c\x93\xc3\xc6\u06dd7q}\xe1e\u00e1\xb4\xfeQ\x95,\b\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8c\x99\x95\x91\xfdr\xefq\x11\xef\xcaz\x9e\x97\xa25k;\x00\n\x89\xddd\xe2\xaa\ngP\x00\x00\u07d4\x8c\xa6\x98\x97F\xb0n2\xe2Hta\xb1\u0399j':\xcf\u05c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8c\xb3\xaa?\xcd!(T\xd7W\x8f\xcc0\xfd\xed\xe6t*1*\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x8c\xc0\xd7\xc0\x16\xfaz\xa9P\x11J\xa1\xdb\tH\x82\xed\xa2t\xea\x89\b\xa9\xab\xa5W\xe3l\x00\x00\u07d4\x8c\xc6R\xdd\x13\xe7\xfe\x14\u06bb\xb3m]2\r\xb9\xff\xee\x8aT\x89a\t=|,m8\x00\x00\u07d4\x8c\u02bf%\a\u007f:\xa4\x15E4MS\xbe\x1b+\x9c3\x90\x00\x89[\xe8f\xc5b\xc5D\x00\x00\u07d4\x8c\xcf:\xa2\x1a\xb7BWj\xd8\xc4\"\xf7\x1b\xb1\x88Y\x1d\ua28965\u026d\xc5\u07a0\x00\x00\u07d4\x8c\xd0\xcd\"\xe6 \xed\xa7\x9c\x04a\xe8\x96\xc9<D\x83~)h\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8c\u078bs.`#\x87\x8e\xb2>\xd1b)\x12K_z\xfb\xec\x89\a?u\u0460\x85\xba\x00\x00\u07d4\x8c\xe2/\x9f\xa3rD\x9aB\x06\x10\xb4z\xe0\xc8\xd5eH\x122\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x8c\u451d\x8a\x16T-B<\x17\x98Ng9\xfar\u03b1w\x8a\x05K@Y&\xf4\xa6=\x80\x00\u07d4\x8c\xe5\xe3\xb5\xf5\x91\xd5\uc8ca\xbf\"\x8f.<5\x13K\xda\xc0\x89}\xc3[\x84\x89|8\x00\x00\xe0\x94\x8c\xee8\xd6YW\x88\xa5n?\xb9F4\xb3\xff\xe1\xfb\xdb&\u058a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8c\xee\xa1^\xec;\xda\xd8\x02?\x98\xec\xf2[+\x8f\xef'\xdb)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8c\xf3To\xd1\u0363=X\x84_\xc8\xfc\xfe\u02bc\xa7\xc5d*\x89\x1f\x1e9\x93,\xb3'\x80\x00\u07d4\x8c\xf6\xda\x02\x04\xdb\u0106\vF\xad\x97?\xc1\x11\x00\x8d\x9e\fF\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8c\xfe\xde\xf1\x98\xdb\n\x91C\xf0\x91)\xb3\xfdd\u073b\x9bIV\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x04\xa5\xeb\xfb]\xb4\t\xdb\x06\x17\xc9\xfaV1\xc1\x92\x86\x1fJ\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x8d\x06\xe4d$\\\xadaI9\xe0\xaf\bE\xe6\xd70\xe2\x03t\x89\n\u070a(\xf3\xd8}\x80\x00\u07d4\x8d\a\xd4-\x83\x1c-|\x83\x8a\xa1\x87+:\xd5\xd2w\x17h#\x89\x12\xee\x1f\x9d\xdb\xeeh\x00\x00\u07d4\x8d\v\x9e\xa5?\xd2cA^\xac\x119\x1f|\xe9\x12<Dpb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x17\x94\xdaP\x9c\xb2\x97\x056a\xa1J\xa8\x92321\xe3\xc1\x89\n\xd2\x01\xa6yO\xf8\x00\x00\u07d4\x8d\x1a\xbd\x89}\xac\xd41.\x18\b\f\x88\xfb\x96G\xea\xb4@R\x89\v\xb5\x9a'\x95<`\x00\x00\u07d4\x8d#\x034\x1e\x1e\x1e\xb5\xe8\x18\x9b\xde\x03\xf7:`\xa2\xa5Ha\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8d#\x8e\x03e\x96\x98vC\xd71s\xc3{\n\xd0`U\xb9l\x89qH\xbf\n*\xf0f\x00\x00\xe0\x94\x8d.1\xb0\x88\x03\xb2\xc5\xf1=9\x8e\xca\u0605( \x9f`W\x8a\x02\x1d\xb8\xbb\xca\xd1\x1e\x84\x00\x00\u07d4\x8d7\x8f\x0e\xdc\v\xb0\xf0hmj \xbejv\x92\xc4\xfa$\xb8\x89\x05k\xc7^-c\x10\x00\x00\u0794\x8dK`<]\xd4W\f4f\x95\x15\xfd\xccfX\x90\x84\fw\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x8dQ\xa4\xccb\x01\x13\"\u0196\xfdr[\x9f\xb8\xf5?\xea\xaa\a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8dTL2\xc0\u007f\u0404,v\x1dS\xa8\x97\xd6\xc9P\xbbu\x99\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8d^\xf1r\xbfw1^\xa6N\x85\xd0\x06\x19\x86\u01d4\xc6\xf5\x19\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x8dak\x1e\xeew\xee\xf6\xf1v\xe0i\x8d\xb3\xc0\xc1A\xb2\xfc\x8f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8dap\xfff\x97\x8ew;\xb6!\xbfr\xb1\xba{\xe3\xa7\xf8~\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8db\v\xde\x17\"\x8fl\xbb\xa7M\xf6\xbe\x87&M\x98\\\xc1y\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8db\x9c `\x815I\x1bP\x13\xf1\x00%\x86\xa0810\xe5\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\x8dfW\xf5\x97\x11\xb1\xf8\x03\xc6\xeb\xefh/\x91[b\xf9-\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\x8dfv7\xe2\x9e\xca\x05\xb6\xbf\xbe\xf1\xf9mF\x0e\uffd9\x84\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8dm\xf2\tHM{\x94p+\x03\xa5>V\xb9\xfb\x06`\xf6\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8dy\\_JV\x89\xadb\u0696\x16q\xf0(\x06R\x86\xd5T\x89o\x05\xb5\x9d; \x00\x00\x00\u07d4\x8d\u007f>a)\x9c-\xb9\xb9\xc0H|\xf6'Q\x9e\xd0\n\x91#\x89^t\xa8P^\x80\xa0\x00\x00\xe0\x94\x8d\x89\x17\v\x92\xb2\xbe,\b\xd5|H\xa7\xb1\x90\xa2\xf1Fr\x0f\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\x8d\x93\xda\u01c5\xf8\x8f\x1a\x84\xbf\x92}Se+E\xa1T\xcc\u0749\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x8d\x99R\u043bN\xbf\xa0\xef\xd0\x1a:\xa9\xe8\xe8\u007f\x05%t.\x89\xbb\x91%T\"c\x90\x00\x00\u07d4\x8d\x9a\fp\xd2& B\xdf\x10\x17\xd6\xc3\x03\x13 $w'\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\x9e\xd7\xf4U0X\xc2ox6\xa3\x80-0d\xeb\x1b6=\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\x8d\xa1\x17\x8fU\xd9wr\xbb\x1d$\x11\x1a@JO\x87\x15\xb9]\x89/\x9a\xc3\xf6\xde\x00\x80\x80\x00\u07d4\x8d\xa1\xd3Y\xbal\xb4\xbc\xc5}zCw \xd5]\xb2\xf0\x1cr\x89\x04V9\x18$O@\x00\x00\u07d4\x8d\xab\x94\x8a\xe8\x1d\xa3\x01\xd9r\xe3\xf6\x17\xa9\x12\xe5\xa7Sq.\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x8d\xad\xdfR\xef\xbdt\u0695\xb9i\xa5GoO\xbb\xb5c\xbf\u0489-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x8d\xb1\x85\xfe\x1bp\xa9Jj\b\x0e~#\xa8\xbe\xdcJ\xcb\xf3K\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x8d\xb5\x8e@n -\xf9\xbcp<H\v\xd8\xed$\x8dR\xa02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\xbc>l\xb43\xe1\x94\xf4\x0f\x82\xb4\x0f\xaa\xdb\x1f\x8b\x85a\x16\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x8d\xc1\xd5\x11\x1d\t\xaf%\xfd\xfc\xacE\\|\xec(>mgu\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8d\u0504\xff\x8a0sd\xebf\xc5%\xa5q\xaa\xc7\x01\xc5\xc3\x18\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\u05a9\xba\xe5\u007fQ\x85I\xad\xa6wFo\ua2b0O\u0674\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde<\xb8\x11\x85h\xefE\x03\xfe\x99\x8c\xcd\xf56\xbf\x19\xa0\x98\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8d\xde`\xeb\b\xa0\x99\xd7\u06a3V\u06aa\xb2G\r{\x02Zk\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\x8d\xf39!Kj\u0472Fc\xceq`4t\x9dn\xf88\u064a\x02TO\xaaw\x80\x90\xe0\x00\x00\xe0\x94\x8d\xf5=\x96\x19\x14q\xe0Y\xdeQ\xc7\x18\xb9\x83\xe4\xa5\x1d*\xfd\x8a\x06\u01b95\xb8\xbb\xd4\x00\x00\x00\u07d4\x8d\xfb\xaf\xbc\x0e[\\\x86\xcd\x1a\u0597\xfe\xea\x04\xf41\x88\u0796\x89\x15%+\u007f_\xa0\xde\x00\x00\u07d4\x8e\a;\xad%\xe4\"\x18a_J\x0ek.\xa8\xf8\xde\"0\xc0\x89\x82=b\x9d\x02k\xfa\x00\x00\u07d4\x8e\x0f\xee8hZ\x94\xaa\xbc\xd7\u0385{k\x14\t\x82Ou\xb8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e#\xfa\xcd\x12\xc7e\xc3j\xb8\x1am\xd3M\x8a\xa9\xe6\x89\x18\xae\x89\t\x11\u418d\xba\x9b\x00\x00\xe0\x94\x8e/\x904\xc9%G\x19\u00ceP\u026ad0^\u0596\xdf\x1e\x8a\x01\x00N.E\xfb~\xe0\x00\x00\u07d4\x8e2@\xb0\x81\x0e\x1c\xf4\a\xa5\x00\x80G@\u03cdad2\xa4\x89\x02/fU\xef\v8\x80\x00\u07d4\x8eHj\x04B\xd1q\xc8`[\xe3H\xfe\xe5~\xb5\b^\xff\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8eaV3k\xe2\u037e2\x14\r\xf0\x8a+\xa5_\u0425\x84c\x89\x04\t\x9e\x1dcW\x18\x00\x00\u07d4\x8eg\b\x15\xfbg\xae\xae\xa5{\x86SN\xdc\x00\xcd\xf5d\xfe\u5272\xe4\xb3#\xd9\xc5\x10\x00\x00\u07d4\x8emt\x85\xcb\u942c\xc1\xad\x0e\xe9\xe8\xcc\xf3\x9c\f\x93D\x0e\x893\xc5I\x901r\f\x00\x00\xe0\x94\x8et\xe0\u0477~\xbc\x82:\xca\x03\xf1\x19\x85L\xb1 '\xf6\u05ca\x16\xb3R\xda^\x0e\xd3\x00\x00\x00\u07d4\x8ex\xf3QE}\x01oJ\xd2u^\xc7BN\\!\xbamQ\x89\a\xea(2uw\b\x00\x00\u07d4\x8ey6\u0552\x00\x8f\xdcz\xa0N\xde\xebuZ\xb5\x13\u06f8\x9d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\u007f\xd28H\xf4\xdb\a\x90j}\x10\xc0K!\x80;\xb0\x82'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8e\x92\xab\xa3\x8er\xa0\x98\x17\v\x92\x95\x92FSz.UV\xc0\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x8e\x98ve$\xb0\xcf'G\xc5\r\xd4;\x95gYM\x971\u0789lD\xb7\xc2a\x82(\x00\x00\u07d4\x8e\x9b5\xadJ\n\x86\xf7XDo\xff\xde4&\x9d\x94\f\xea\u0349\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8e\x9c\b\xf78f\x1f\x96v#n\xff\x82\xbaba\xdd?H\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\x9cB\x92f\xdf\x05~\xfax\xdd\x1d_w\xfc@t*\xd4f\x89\x10D.\u0475l|\x80\x00\u07d4\x8e\xa6V\xe7\x1e\xc6Q\xbf\xa1|ZWY\xd8`1\xcc5\x99w\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x8e\xae)CU\x98\xba\x8f\x1c\x93B\x8c\xdb>+M1\a\x8e\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb1\xfb\xe4\xe5\xd3\x01\x9c\xd7\xd3\r\xae\x9c\r[Lv\xfbc1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\xb5\x17t\xaf k\x96k\x89\t\xc4Z\xa6r'H\x80,\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xb8\xc7\x19\x82\xa0\x0f\xb8Bu)2S\xf8\x04ED\xb6kI\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\x8e\xcb\u03ec\xbf\xaf\xe9\xf0\f9\"\xa2N,\xf0\x02gV\xca \x8a\x011\xbe\xb9%\xff\xd3 \x00\x00\u07d4\x8e\u03b2\xe1$Sl[_\xfcd\x0e\xd1O\xf1^\u0668\xcbq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8e\u042f\x11\xff(p\xda\x06\x81\x00J\xfe\x18\xb0\x13\xf7\xbd8\x82\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\x8e\xd1Cp\x1f/r(\x0f\xd0J{Ad(\x19y\xea\x87\u0248\xc2I\xfd\xd3'x\x00\x00\u07d4\x8e\xd1R\x8bD~\xd4)y\x02\xf69\xc5\x14\u0414J\x88\xf8\u0209\n\xc6\xe7z\xb6c\xa8\x00\x00\u07d4\x8e\xd4(L\x0fGD\x9c\x15\xb8\u0673$]\u8fb6\u0380\xbf\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\x8e\xde~=\xc5\aI\xc6\xc5\x0e.(\x16\x84x\xc3M\xb8\x19F\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\x8e\xe5\x843}\xdb\xc8\x0f\x9e4\x98\xdfU\xf0\xa2\x1e\xac\xb5\u007f\xb1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x8e\xeb\xec\x1ab\xc0\x8b\x05\xa7\xd1\u0551\x80\xaf\x9f\xf0\u044e?6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x8e\xf4\u0622\xc2<Ry\x18{d\xe9ot\x14\x04\bS\x85\xf3\x89\x10=\xc1\xe9\xa9i{\x00\x00\u07d4\x8e\xf7\x11\xe4:\x13\x91\x8f\x13\x03\xe8\x1d\x0e\xa7\x8c\x9e\xef\xd6~\xb2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8e\xfe\xc0X\xccTaWvjc'u@J3J\xaa\u0687\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\x8f\x02\xbd\xa6\xc3i\"\xa6\xbejP\x9b\xe5\x19\x06\u04d3\xf7\xb9\x9b\x897I\r\xc1.\xbe\u007f\x80\x00\u07d4\x8f\x058\xedq\xda\x11U\xe0\xf3\xbd\xe5f|\xeb\x841\x8a\x1a\x87\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\x8f\x06||\x1b\xbdWx\v{\x9e\xeb\x9e\xc0\x03/\x90\xd0\xdc\xf9\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x8f\n\xb8\x94\xbd?Ni}\xbc\xfb\x85\x9dIz\x9b\xa1\x95\x99J\x8a\b]c\x8beG*\xa2\x00\x00\u07d4\x8f\n\xf3uf\xd1R\x80/\x1a\xe8\xf9(\xb2Z\xf9\xb19\xb4H\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x8f\x19R\xee\xd1\xc5H\xd9\ue6d7\xd0\x16\x9a\a\x93;\xe6\x9fc\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x8f\x1f\xcc<Q\xe2R\xb6\x93\xbc[\x0e\xc3\xf65)\xfei(\x1e\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x8f\"`\x96\xc1\x84\xeb\xb4\x01\x05\xe0\x8d\xacM\"\xe1\xc2\xd5M0\x89\x10\x9eC{\xd1a\x8c\x00\x00\xe0\x94\x8f)\xa1J\x84Z\xd4X\xf2\xd1\b\xb5h\xd8\x13\x16k\xcd\xf4w\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x8f1\xc7\x00Q\x97\xec\x99z\x87\xe6\x9b\xecHd\x9a\xb9K\xb2\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fA\xb1\xfb\xf5B\x98\xf5\u043c-\x12/N\xb9]\xa4\xe5\xcd=\x89\x133\x83/^3\\\x00\x00\u07d4\x8fG2\x8e\xe02\x01\xc9\xd3^\u04b5A+%\xde\u0305\x93b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8fG=\n\xb8v\u076a\x15`\x86!\xd7\x01>o\xf7\x14\xb6u\x89\x19\x80\x1c\x83\xb6\xc7\xc0\x00\x00\u07d4\x8fM\x1dAi>F,\xf9\x82\xfd\x81\u042ap\x1d:St\u0249\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fM\x1e~Ea(J4\xfe\xf9g<\r4\xe1*\xf4\xaa\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8fO\xb1\xae\xa7\xcd\x0fW\x0e\xa5\xe6\x1b@\xa4\xf4Q\vbd\xe4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x8fV\x1bA\xb2\t\xf2H\u0229\x9f\x85\x87\x887bP`\x9c\xf3\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\x8fX\xd84\x8f\xc1\xdcN\r\xd84;eC\xc8W\x04^\xe9@\x8a\x02\xe3\x03\x8d\xf4s\x03(\x00\x00\u07d4\x8f`\x89_\xbe\xbb\xb5\x01\u007f\xcb\xff<\u0763\x97)+\xf2[\xa6\x89\x17D\x06\xff\x9fo\u0480\x00\u07d4\x8fd\xb9\xc1$m\x85x1d1\a\xd3U\xb5\xc7_\xef]O\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\x8ff\x0f\x8b.L|\u00b4\xac\x9cG\xed(P\x8d_\x8f\x86P\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x8fi\xea\xfd\x023\xca\xdb@Y\xabw\x9cF\xed\xf2\xa0PnH\x89`\xf0f \xa8IE\x00\x00\xe0\x94\x8fq~\xc1U/LD\x00\x84\xfb\xa1\x15J\x81\xdc\x00>\xbd\xc0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\x8f\x8a\xcb\x10v\a8\x84y\xf6K\xaa\xab\xea\x8f\xf0\a\xad\xa9}\x8a\x05\xc6\xf3\b\n\xd4#\xf4\x00\x00\u07d4\x8f\x8c\xd2n\x82\xe7\xc6\xde\xfd\x02\u07ed\a\x97\x90!\xcb\xf7\x15\f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x8f\x8f7\u042d\x8f3]*q\x01\xb4\x11V\xb6\x88\xa8\x1a\x9c\xbe\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\x8f\x92\x84O(*\x92\x99\x9e\u5d28\xd7s\xd0kiM\xbd\x9f\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x8f\xact\x8fxJ\x0f\xedh\u06e43\x19\xb4*u\xb4d\x9cn\x891T\xc9r\x9d\x05x\x00\x00\u07d4\x8f\u0665\xc3:}\x9e\xdc\xe0\x99{\xdfw\xab0d$\xa1\x1e\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xef\xfa\xdb8z\x15G\xfb(M\xa9\xb8\x14\u007f>|m\xc6\u0689-b{\xe4S\x05\b\x00\x00\u07d4\x8f\xf4`Ehw#\xdc3\xe4\u0419\xa0i\x04\xf1\ubd44\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\x8f\xfa\x06!\"\xac0t\x18\x82\x1a\u06d3\x11\aZ7\x03\xbf\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x8f\xfe2)\x97\xb8\xe4\x04B-\x19\xc5J\xad\xb1\x8f[\xc8\u9dc9\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x90\x01\x94\u0131\aC\x05\u045d\xe4\x05\xb0\xacx(\x0e\xca\xf9g\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90\x03\xd2p\x89\x1b\xa2\xdfd=\xa84\x15\x83\x195E\xe3\xe0\x00\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x90\x05z\xf9\xaaf0~\xc9\xf03\xb2\x97$\u04f2\xf4\x1e\xb6\xf9\x8a\x19\xd1\u05aa\xdb,R\xe8\x00\x00\u07d4\x90\x0f\v\x8e5\xb6h\xf8\x1e\xf2R\xb18U\xaaP\a\xd0\x12\xe7\x89\x17\n\x0fP@\xe5\x04\x00\x00\u07d4\x90\x18\xcc\x1fH\xd20\x8e%*\xb6\b\x9f\xb9\x9a|\x1dV\x94\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x90\x1d\x99\xb6\x99\xe5\u0191\x15\x19\xcb v\xb4\xc7c0\xc5M\"\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x90-t\xa1W\xf7\u04b9\xa37\x8b\x1fVp70\xe0:\x17\x19\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x904\x13\x87\x8a\xea;\xc1\bc\t\xa3\xfev\x8beU\x9e\x8c\xab\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x90If\xcc\"\x13\xb5\xb8\xcb[\xd6\b\x9e\xf9\xcd\xdb\xef~\xdf\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\x90L\xaaB\x9ca\x9d\x94\x0f\x8egA\x82j\r\xb6\x92\xb1\x97(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x90R\xf2\xe4\xa3\xe3\xc1-\xd1\xc7\x1b\xf7\x8aN\xc3\x04=\u020b~\x89\x0e~\xeb\xa3A\vt\x00\x00\u0794\x90U&V\x8a\xc1#\xaf\xc0\xe8J\xa7\x15\x12O\xeb\xe8=\xc8|\x88\xf8i\x93)g~\x00\x00\u07d4\x90\x92\x91\x87\a\xc6!\xfd\xbd\x1d\x90\xfb\x80\xebx\u007f\xd2osP\x89\x85[[\xa6\\\x84\xf0\x00\x00\u07d4\x90\x9b^v:9\xdc\u01d5\"=s\xa1\u06f7\xd9L\xa7Z\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\x90\xac\xce\xd7\xe4\x8c\b\u01b94dm\xfa\n\xdf)\u0714\aO\x89\x03\vK\x15{\xbdI\x00\x00\u07d4\x90\xb1\xf3p\xf9\xc1\xeb\v\xe0\xfb\x8e+\x8a\xd9jAcq\u074a\x890\xca\x02O\x98{\x90\x00\x00\u07d4\x90\xb6/\x13\x1a_)\xb4UqQ>\xe7\xa7J\x8f\v#\"\x02\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x90\xbdb\xa0P\x84Ra\xfaJ\x9f|\xf2A\xeac\v\x05\ufe09\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x90\xc4\x1e\xba\x00\x8e \xcb\xe9'\xf3F`?\u0206\x98\x12Yi\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x90\u0480\x9a\xe1\xd1\xff\xd8\xf6>\xda\x01\xdeI\xddU-\xf3\u047c\x89\u063beI\xb0+\xb8\x00\x00\u07d4\x90\xdc\t\xf7\x17\xfc*[i\xfd`\xba\b\xeb\xf4\v\xf4\xe8$l\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x90\xe3\x00\xacqE\x1e@\x1f\x88\u007fnw(\x85\x16G\xa8\x0e\a\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x90\xe3Z\xab\xb2\xde\xef@\x8b\xb9\xb5\xac\xefqDW\xdf\xdebr\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\x90\xe7\a\x0fM\x03?\xe6\x91\f\x9e\xfeZ'\x8e\x1f\xc6#M\xef\x89\x05q8\b\x19\xb3\x04\x00\x00\u07d4\x90\xe9>M\xc1q!HyR36\x14\x00+\xe4#VI\x8e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x90\u9a68.\u06a8\x14\u0084\xd22\xb6\u9e90p\x1dIR\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\x90\xf7t\xc9\x14}\u0790\x85=\xdcC\xf0\x8f\x16\xd4U\x17\x8b\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x90\xfcS{!\x06Xf\n\x83\xba\xa9\xacJ\x84\x02\xf6WF\xa8\x89e\xea=\xb7UF`\x00\x00\u07d4\x91\x05\n\\\xff\xad\xed\xb4\xbbn\xaa\xfb\xc9\xe5\x014(\xe9l\x80\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x91\x05\x17d\xafk\x80\x8eB\x12\xc7~0\xa5W.\xaa1pp\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x91\v}Wz~9\xaa#\xac\xf6*\xd7\xf1\xef4)4\xb9h\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x91\x0e\x99eC4Lh\x15\xfb\x97\u0367\xafK\x86\x98vZ[\x89\x05\x9a\xf6\x98)\xcfd\x00\x00\u07d4\x91\x1f\xee\xa6\x1f\xe0\xedP\u0179\xe5\xa0\xd6`q9\x9d(\xbd\u0189\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x91\x1f\xf23\xe1\xa2\x11\xc0\x17,\x92\xb4l\xf9\x97\x03\x05\x82\xc8:\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x91 \xe7\x11s\xe1\xba\x19\xba\x8f\x9fO\xdb\u072a4\xe1\u05bbx\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91!\x17\x12q\x9f+\bM;8u\xa8Pi\xf4f61A\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91#\x04\x11\x8b\x80G=\x9e\x9f\xe3\xeeE\x8f\xbea\x0f\xfd\xa2\xbb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x91Tky\xec\xf6\x9f\x93kZV\x15\b\xb0\xd7\xe5\f\u0159/\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x91V\u0440)5\x0eG\x04\b\xf1_\x1a\xa3\xbe\x9f\x04\ng\u018965\u026d\xc5\u07a0\x00\x00\u07d4\x91b\x0f>\xb3\x04\xe8\x13\u048b\x02\x97Ume\xdcN]\u5a89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\x91k\xf7\xe3\xc5E\x92\x1d2\x06\xd9\x00\xc2O\x14\x12|\xbd^p\x8a\x03\xd0\u077c}\xf2\xbb\x10\x00\x00\u0794\x91l\xf1}qA(\x05\xf4\xaf\xc3DJ\v\x8d\xd1\xd93\x9d\x16\x88\xc6s\xce<@\x16\x00\x00\u07d4\x91{\x8f\x9f:\x8d\t\xe9 ,R\u009erA\x96\xb8\x97\xd3^\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\x91\x89g\x91\x8c\u0617\xdd\x00\x05\xe3m\xc6\u0203\xefC\x8f\xc8\u01c9\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\x91\x89\x8e\xab\x8c\x05\xc0\"(\x83\xcdM\xb2;w\x95\xe1\xa2J\u05c9lk\x93[\x8b\xbd@\x00\x00\u0794\x91\x91\xf9F\x98!\x05\x16\xcfc!\xa1B\a\x0e Yvt\xed\x88\xee\x9d[\xe6\xfc\x11\x00\x00\u07d4\x91\xa4\x14\x9a,{\x1b:g\xea(\xaf\xf3G%\u0fcdu$\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x91\xa7\x87\xbcQ\x96\xf3HW\xfe\f7/M\xf3v\xaa\xa7f\x13\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xa8\xba\xae\xd0\x12\xea.c\x80;Y=\r\f*\xabL[\n\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x91\xac\\\xfeg\xc5J\xa7\xeb\xfb\xa4HflF\x1a;\x1f\xe2\xe1\x89\x15\xc94\x92\xbf\x9d\xfc\x00\x00\u07d4\x91\xbb?y\x02+\xf3\xc4S\xf4\xff%n&\x9b\x15\xcf,\x9c\xbd\x89RX\\\x13\xfe:\\\x00\x00\u07d4\x91\xc7^<\xb4\xaa\x89\xf3F\x19\xa1d\xe2\xa4x\x98\xf5gM\x9c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x91\xc8\f\xaa\b\x1b85\x1d*\x0e\x0e\x00\xf8\n4\xe5dt\xc1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x91\xccF\xaa7\x9f\x85jf@\xdc\xcdZd\x8ay\x02\xf8I\u0649\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x91\u04a9\xee\x1am\xb2\x0fS\x17\u0327\xfb\xe218\x95\u06ce\xf8\x8a\x01\xcc\u00e5/0n(\x00\x00\u07d4\x91\xd6n\xa6(\x8f\xaaK=`l*\xa4\\{k\x8a%'9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\u06f6\xaa\xad\x14\x95\x85\xbeG7\\]m\xe5\xff\t\x19\x15\x18\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x91\xe8\x81\x06R\xe8\xe6\x16\x15%\xd6;\xb7u\x1d\xc2\x0fg`v\x89'Mej\xc9\x0e4\x00\x00\u07d4\x91\xf5\x16\x14l\xda (\x17\x19\x97\x80`\u01beAI\x06|\x88\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x91\xf6$\xb2J\x1f\xa5\xa0V\xfeW\x12)\xe77\x9d\xb1K\x9a\x1e\x8a\x02\x8a\x85\x17\xc6i\xb3W\x00\x00\xe0\x94\x91\xfe\x8aLad\u07cf\xa6\x06\x99]k\xa7\xad\xca\xf1\u0213\u038a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4\x92\x1fRa\xf4\xf6\x12v\a\x06\x89&%\xc7^{\u0396\xb7\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92!\xc9\xce\x01#&et\x10\x96\xac\a#Y\x03\xad\x1f\xe2\xfc\x89\x06\xdbc3U\"b\x80\x00\u07d4\x92%\x988`\xa1\xcbF#\xc7$\x80\xac\x16'+\f\x95\xe5\xf5\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x92%\xd4jZ\x80\x949$\xa3\x9e[\x84\xb9m\xa0\xacE\x05\x81\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x92* \u01da\x1d:&\xdd8)g{\xf1\xd4\\\x8fg+\xb6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x92C\x8eR\x03\xb64o\xf8\x86\xd7\xc3b\x88\xaa\xcc\xccx\xce\u028965\u026d\xc5\u07a0\x00\x00\u07d4\x92C\xd7v-w({\x12c\x86\x88\xb9\x85N\x88\xa7i\xb2q\x8965\u026d\xc5\u07a0\x00\x00\u0794\x92K\xcez\x85<\x97\v\xb5\xec{\xb7Y\xba\xeb\x9ct\x10\x85{\x88\xbe -j\x0e\xda\x00\x00\u07d4\x92N\xfam\xb5\x95\xb7\x93\x13'~\x881\x96%\akX\n\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92U\x82&\xb3\x84bl\xadH\xe0\x9d\x96k\xf19^\xe7\xea]\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\x92`\x82\xcb~\xedK\x19\x93\xad$ZGrg\xe1\xc3<\xd5h\x89\x14Jt\xba\u07e4\xb6\x00\x00\u07d4\x92b\t\xb7\xfd\xa5N\x8d\u06dd\x9eM=\x19\xeb\u070e\x88\u009f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92h\xd6&FV6\x11\xdc;\x83*0\xaa#\x94\xc6F\x13\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92i\x8e4Sx\xc6-\x8e\xda\x18M\x946j\x14K\f\x10[\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\x92y:\u0173rhwJq0\xde+\xbd3\x04\x05f\x17s\x89\x02,\xa3X|\xf4\xeb\x00\x00\xe0\x94\x92y\xb2\"\x8c\xec\x8f{M\xda?2\x0e\x9a\x04f\xc2\xf5\x85\u028a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x92|\xb7\xdc\x18p6\xb5B{\xc7\xe2\x00\xc5\xecE\f\x1d'\u0509\v\xb5\x9a'\x95<`\x00\x00\u07d4\x92|\u00bf\xda\x0e\b\x8d\x02\xef\xf7\v8\xb0\x8a\xa5<\xc3\tA\x89do`\xa1\xf9\x866\x00\x00\xe0\x94\x92\x84\xf9m\xdbG\xb5\x18n\xe5X\xaa12M\xf56\x1c\x0fs\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\x92\x9d6\x8e\xb4j-\x1f\xbd\xc8\xff\xa0`~\xdeK\xa8\x8fY\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa7\u0166Cb\xe9\xf8B\xa2=\xec\xa2\x105\x85\u007f\x88\x98\x00\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\x92\xa8\x98\xd4o\x19q\x9c8\x12j\x8a<'\x86z\xe2\xce\u5589lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xa9q\xa79y\x9f\x8c\xb4\x8e\xa8G]r\xb2\xd2GAr\xe6\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x92\xaa\xe5\x97h\xed\xdf\xf8<\xfe`\xbbQ.s\n\x05\xa1a\u05c9\\\x97xA\fv\u0440\x00\u07d4\x92\xad\x1b=u\xfb\xa6}Tf=\xa9\xfc\x84\x8a\x8a\xde\x10\xfag\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xae[|~\xb4\x92\xff\x1f\xfa\x16\xddB\xad\x9c\xad@\xb7\xf8\u0709.\xe4IU\b\x98\xe4\x00\x00\u07d4\x92\xc0\xf5s\xec\xcfb\xc5H\x10\xeek\xa8\xd1\xf1\x13T+0\x1b\x89\xb7ro\x16\u0331\xe0\x00\x00\u07d4\x92\xc1?\xe0\xd6\u0387\xfdP\xe0=\uf7e6@\x05\t\xbdps\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x92\xc9L( \xdf\xcfqV\xe6\xf10\x88\xec\u754b6v\xfd\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\x92\xcf\xd6\x01\x88\xef\u07f2\xf8\xc2\xe7\xb1i\x8a\xbb\x95&\xc1Q\x1f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\u062d\x9aMah;\x80\u0526g.\x84\xc2\rbB\x1e\x80\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x92\u0725\xe1\x02\xb3\xb8\x1b`\xf1\xa5\x04cIG\xc3t\xa8\x8c\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x92\xe454\x0e\x9d%<\x00%c\x89\xf5+\x06}U\x97Nv\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x92\xe49(\x16\xe5\xf2\xef_\xb6X7\xce\xc2\xc22\\\xc6I\"\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x92\xe6X\x1e\x1d\xa1\xf9\xb8F\xe0\x93G3=\xc8\x18\xe2\u04acf\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\x93\x1d\xf3M\x12%\xbc\xd4\"Nch\r\\L\t\xbc\xe75\xa6\x89\x03\xaf\xb0\x87\xb8v\x90\x00\x00\u07d4\x93\x1f\xe7\x12\xf6B\a\xa2\xfdP\"r\x88CT\x8b\xfb\x8c\xbb\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x93#_4\r(c\xe1\x8d/LR\x99e\x16\x13\x8d\"\x02g\x89\x04\x00.D\xfd\xa7\xd4\x00\x00\u07d4\x93%\x82U\xb3|\u007fX\xf4\xb1\x06s\xa92\xdd:\xfd\x90\xf4\xf2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93(\xd5\\\xcb?\xceS\x1f\x19\x93\x823\x9f\x0eWn\xe8@\xa3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x93)\xff\xdc&\x8b\xab\u0788t\xb3f@l\x81D[\x9b-5\x89\x16\xe6/\x8cs\f\xa1\x80\x00\u07d4\x93+\x9c\x04\xd4\r*\xc80\x83\xd9B\x98\x16\x9d\xae\x81\xab.\u0409lk\x93[\x8b\xbd@\x00\x00\u07d4\x9346\xc8G&U\xf6L:\xfa\xaf|Lb\x1c\x83\xa6+8\x8965\u026d\xc5\u07a0\x00\x00\u0794\x93;\xf3?\x82\x99p+:\x90&B\xc3>\v\xfa\xea\\\x1c\xa3\x88\xd2\xf1?w\x89\xf0\x00\x00\u07d4\x93@4\\\xa6\xa3\uaf77sc\xf2X`C\xf2\x948\xce\v\x89\x1c\xc8\x05\xda\r\xff\xf1\x00\x00\xe0\x94\x93@\xb5\xf6x\xe4^\xe0^\xb7\b\xbbz\xbbn\xc8\xf0\x8f\x1bk\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x93J\xf2\x1b~\xbf\xa4g\xe2\xce\xd6Z\xa3N\xdd:\x0e\xc7\x132\x8a\a\x80\x1f>\x80\xcc\x0f\xf0\x00\x00\xe0\x94\x93PiDJj\x98M\xe2\bNFi*\xb9\x9fg\x1f\xc7'\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\xe0\x94\x93P~\x9e\x81\x19\xcb\xce\u068a\xb0\x87\xe7\xec\xb0q8=i\x81\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\x93g\x8a<W\x15\x1a\xebh\xef\xdcC\xefM6\xcbY\xa0\t\xf3\x89\x01\xa1*\x92\xbc<>\x00\x00\xe0\x94\x93m\xcf\x00\x01\x94\xe3\xbf\xf5\n\u0174$:;\xa0\x14\xd6a\u060a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x93o8\x13\xf5\xf6\xa1;\x8eO\xfe\xc8?\xe7\xf8&\x18jq\u0349\x1c0s\x1c\xec\x03 \x00\x00\u07d4\x93t\x86\x9dJ\x99\x11\xee\x1e\xafU\x8b\xc4\u00b6>\xc6:\xcf\u074965\u026d\xc5\u07a0\x00\x00\u07d4\x93uc\u0628\x0f\u05657\xb0\xe6m \xa0%%\xd5\u0606`\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\x93v\xdc\xe2\xaf.\xc8\xdc\xdat\x1b~sEfF\x81\xd96h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x93\x86\x8d\xdb*yM\x02\xeb\xda/\xa4\x80|v\xe3`\x98X\u0709m\xee\x15\xfc|$\xa7\x80\x00\xe0\x94\x93\x9cC\x13\xd2(\x0e\xdf^\a\x1b\xce\xd8F\x06?\n\x97]T\x8a\x19i6\x89t\xc0[\x00\x00\x00\xe0\x94\x93\xa6\xb3\xabB0\x10\xf9\x81\xa7H\x9dJ\xad%\xe2b\\WA\x8a\x04F\x80\xfej\x1e\xdeN\x80\x00\u07d4\x93\xaa\x8f\x92\xeb\xff\xf9\x91\xfc\x05^\x90ne\x1a\xc7h\xd3+\u02092\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x93\xb4\xbf?\xdf\xf6\xde?NV\xbamw\x99\xdcK\x93\xa6T\x8f\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\x93\xbc}\x9aJ\xbdD\u023b\xb8\xfe\x8b\xa8\x04\xc6\x1a\xd8\xd6Wl\x89\xd8\xd6\x11\x9a\x81F\x05\x00\x00\u07d4\x93\xc2\xe6N]\xe5X\x9e\xd2P\x06\xe8C\x19n\xe9\xb1\xcf\v>\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\x93\u020e-\x88b\x1e0\xf5\x8a\x95\x86\xbe\xd4\t\x89\x99\xebg\u074a\x06\x9bZ\xfa\xc7P\xbb\x80\x00\x00\u07d4\x93\xe0\xf3~\xcd\xfb\x00\x86\xe3\xe8b\xa9p4D{\x1eM\xec\x1a\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\x93\xe3\x03A\x1a\xfa\xf6\xc1\a\xa4A\x01\u026c[6\xe9\xd6S\x8b\x8a\r\xf9\xdd\xfe\xcd\x03e@\x00\x00\u07d4\x93\xf1\x8c\xd2R`@v\x14\x88\xc5\x13\x17M\x1eycv\x8b,\x89\x82\xff\xac\x9a\u0553r\x00\x00\u07d4\x94\x0fqQ@P\x9f\xfa\xbf\x97EF\xfa\xb3\x90\"\xa4\x19R\u0489K\xe4\xe7&{j\xe0\x00\x00\u07d4\x94,k\x8c\x95[\xc0\u0608\x12g\x8a#g%\xb3'9\xd9G\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\x94=7\x86JJS}5\xc8\u0657#\xcdd\x06\xce%b\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94C\x9c\xa9\xcc\x16\x9ay\u0520\x9c\xae^gvJo\x87\x1a!\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x94D\x9c\x01\xb3*\u007f\xa5Z\xf8\x10OB\xcd\xd8D\xaa\x8c\xbc@\x8a\x03\x81\x11\xa1\xf4\xf0<\x10\x00\x00\xe0\x94\x94E\xba\\0\xe9\x89a\xb8`$a\xd08]@\xfb\xd8\x03\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94O\a\xb9o\x90\xc5\xf0\xd7\xc0\u0140S1I\xf3\xf5\x85\xa0x\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\x94T\xb3\xa8\xbf\xf9p\x9f\xd0\u1407~l\xb6\u0219t\xdb\u0589\x90\xf54`\x8ar\x88\x00\x00\u07d4\x94]\x96\xeaW>\x8d\xf7&+\xbf\xa5r\"\x9bK\x16\x01k\x0f\x89\vX\x9e\xf9\x14\xc1B\x00\x00\u07d4\x94^\x18v\x9d~\xe7'\xc7\x01?\x92\xde$\xd1\x17\x96\u007f\xf3\x17\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94a'\x81\x03;W\xb1F\xeet\xe7S\xc6r\x01\u007fS\x85\xe4\x89\xc3(\t>a\xee@\x00\x00\xe0\x94\x94dJ\xd1\x16\xa4\x1c\xe2\xca\u007f\xbe\xc6\t\xbd\xefs\x8a*\xc7\u01ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x94p\xcc6YE\x86\x82\x18!\xc5\u0256\xb6\xed\xc8;mZ2\x89\x01M\x11 \u05f1`\x00\x00\xe0\x94\x94u\xc5\x10\xec\x9a&\x97\x92GtL=\x8c;\x0e\v_D\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x94~\x11\xe5\xea)\ro\u00f3\x80H\x97\x9e\f\xd4N\xc7\xc1\u007f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\x83\u064f\x14\xa3?\xdc\x11\x8d@9U\u00995\xed\xfc_p\x89\x18\xea;4\xefQ\x88\x00\x00\u07d4\x94\x911\xf2\x89C\x92\\\xfc\x97\xd4\x1e\f\xea\v&)s\xa70\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\x94\x9f\x84\xf0\xb1\xd7\u0127\xcfI\xee\u007f\x8b,J\x13M\xe3(x\x89%\"H\u07b6\xe6\x94\x00\x00\u07d4\x94\x9f\x8c\x10{\xc7\xf0\xac\xea\xa0\xf1pR\xaa\xdb\xd2\xf9s+.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x94\xa7\u0368\xf4\x81\xf9\u061dB\xc3\x03\xae\x162\xb3\xb7\t\xdb\x1d\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x94\xa9\xa7\x16\x911| d'\x1bQ\xc95?\xbd\xed5\x01\xa8\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x94\xadK\xad\x82K\xd0\ub7a4\x9cX\u03bc\xc0\xff^\b4k\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\x94\xbb\xc6}\x13\xf8\x9e\xbc\xa5\x94\xbe\x94\xbcQp\x92\f0\xd9\xf3\x89\x04X\xff\xa3\x15\nT\x00\x00\u07d4\x94\xbe:\xe5Ob\xd6c\xb0\xd4\u031e\x1e\xa8\xfe\x95V\ua7bf\x89\x01C\x13,\xa8C\x18\x00\x00\xe0\x94\x94\xc0U\xe8X5z\xaa0\xcf A\xfa\x90Y\xce\x16J\x1f\x91\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\xe0\x94\x94\xc7B\xfdz\x8by\x06\xb3\xbf\xe4\xf8\x90O\xc0\xbe\\v\x803\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x94\xcaV\xdew\u007f\xd4S\x17\u007f^\x06\x94\xc4x\xe6j\xff\x8a\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x94\xd8\x10t\xdbZ\xe1\x97\u04bb\x13s\xab\x80\xa8}\x12\x1cK\u04ca\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x94\u06c0xs\x86\n\xac=Z\xea\x1e\x88^R\xbf\xf2\x86\x99T\x89\xae\x8ez\v\xb5u\xd0\x00\x00\u07d4\x94\xe1\xf5\u02db\x8a\xba\xce\x03\xa1\xa6B\x82VU;i\f#U\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x94\xef\x8b\xe4Pw\xc7\xd4\xc5e'@\u0794jbbOq?\x89\x05l\xf5Y:\x18\xf8\x80\x00\u07d4\x94\xf1?\x9f\b6\xa3\xee$7\xa8I\"\u0498M\xc0\xf7\xd5;\x89\xa2\xa02\x9b\u00ca\xbe\x00\x00\u07d4\x94\xf8\xf0W\xdb~`\xe6u\xad\x94\x0f\x15X\x85\u0464w4\x8e\x89\x15\xbeat\xe1\x91.\x00\x00\xe0\x94\x94\xfc\u03ad\xfe\\\x10\x9c^\xae\xafF-C\x871B\u020e\"\x8a\x01\x045a\xa8\x82\x93\x00\x00\x00\u07d4\x95\x03N\x16!\x86Q7\xcdG9\xb3F\xdc\x17\xda:'\xc3N\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\x95\fh\xa4\t\x88\x15M#\x93\xff\xf8\xda|\u0369\x96\x14\xf7,\x89\xf9AF\xfd\x8d\xcd\xe5\x80\x00\xe0\x94\x95\x0f\xe9\xc6\xca\xd5\f\x18\xf1\x1a\x9e\xd9\xc4W@\xa6\x18\x06\x12\u040a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x95!\x83\xcf\u04ce5.W\x9d6\xde\xce\u0171\x84P\xf7\xfb\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95'\x8b\b\xde\xe7\xc0\xf2\xc8\xc0\xf7\"\xf9\xfc\xbb\xb9\xa5$\x1f\u0689\x82\x93\t\xf6O\r\xb0\x00\x00\u07d4\x95,W\xd2\xfb\x19Q\a\xd4\xcd\\\xa3\x00wA\x19\u07ed/x\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x955r\xf0\xeam\xf9\xb1\x97\xca\xe4\x0eK\x8e\xcc\x05lCq\u014965\u026d\xc5\u07a0\x00\x00\u07d4\x95>\xf6R\xe7\xb7i\xf5=nxjX\x95/\xa9>\xe6\xab\u725b\ny\x1f\x12\x110\x00\x00\u07d4\x95DpF1;/:^\x19\xb9H\xfd;\x8b\xed\xc8,q|\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x95]\xb3\xb7C`\xb9\xa2hg~s\u03a8!f\x8a\xf6\xfa\u038a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\x95`\xe8\xacg\x18\xa6\xa1\xcd\xcf\xf1\x89\xd6\x03\xc9\x06>A=\xa6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x95g\xa0\u0781\x1d\xe6\xff\t[~\xe6N\u007f\x1b\x83\xc2a[\x80\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\x95h\x1c\xda\xe6\x9b I\xce\x10\x1e2\\u\x98\x92\xca\xc3\xf8\x11\x89\x9a\xe9*\x9b\xc9L@\x00\x00\xe0\x94\x95h\xb7\xdeuV(\xaf5\x9a\x84T=\xe25\x04\xe1^A\xe6\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x95i\xc6:\x92\x84\xa8\x05bm\xb3\xa3.\x9d#c\x93GaQ\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\x80\x9e\x8d\xa3\xfb\xe4\xb7\xf2\x81\xf0\xb8\xb1q_B\x0f}}c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\x9fW\xfd\xedj\xe3y\x13\xd9\x00\xb8\x1e_H\xa7\x93\"\xc6'\x89\r\xdb&\x10GI\x11\x80\x00\u07d4\x95\x9f\xf1\u007f\x1dQ\xb4s\xb4@\x10\x05'U\xa7\xfa\x8cu\xbdT\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x95\xa5w\xdc.\xb3\xael\xb9\xdf\xc7z\xf6\x97\xd7\xef\xdf\xe8\x9a\x01\x89\a_a\x0fp\xed \x00\x00\u07d4\x95\xcbm\x8acy\xf9J\xba\x8b\x88ViV,MD\x8eV\xa7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x95\xd5PB{ZQLu\x1ds\xa0\xf6\u049f\xb6]\"\xed\x10\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x95\u064d\f\x10i\x90\x8f\x06zR\xac\xac+\x8bSM\xa3z\xfd\x89oY\xb60\xa9)p\x80\x00\xe0\x94\x95\xdfN4E\xd7f&$\u010e\xbat\u03de\nS\xe9\xf72\x8a\v\xdb\xc4\x1e\x03H\xb3\x00\x00\x00\u07d4\x95\xe6\xa5K-_g\xa2JHu\xafu\x10|\xa7\xea\x9f\xd2\xfa\x89Hz\x9a0E9D\x00\x00\xe0\x94\x95\xe6\xf9=\xac\"\x8b\xc7XZ%sZ\xc2\xd0v\xcc:@\x17\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x95\xe7ad$\xcd\ta\xa7\x17'$t7\xf0\x06\x92r(\x0e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x95\xe8\n\x82\xc2\f\xbe= `$,\xb9-sX\x10\xd04\xa2\x89\x01\xc3.F?\u0539\x80\x00\u07d4\x95\xf6-\x02C\xed\xe6\x1d\xad\x9a1e\xf59\x05'\rT\xe2B\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\x95\xfbZ\xfb\x14\xc1\uf6b7\xd1y\xc5\xc3\x00P?\xd6j^\xe2\x89\x01\xda\xf7\xa0+\r\xbe\x80\x00\u07d4\x96\x10Y\"\x02\u0082\xab\x9b\u0628\x84Q\x8b>\v\xd4u\x817\x89\x0e\x87?D\x13<\xb0\x00\x00\xe0\x94\x96\x1cY\xad\xc7E\x05\u0446M\x1e\xcf\u02ca\xfa\x04\x12Y<\x93\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\x96,\r\xec\x8a=FK\xf3\x9b\x12\x15\xea\xfd&H\n\xe4\x90\u0349l\x82\xe3\xea\xa5\x13\xe8\x00\x00\u07d4\x96,\xd2*\x8e\xdf\x1eONU\xb4\xb1]\xdb\xfb]\x9dT\x19q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x963K\xfe\x04\xff\xfaY\x02\x13\xea\xb3e\x14\xf38\xb8d\xb76\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x967\xdc\x12r=\x9cxX\x85B\uac02fO?\x03\x8d\x9d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96N\xabK'kL\u0618>\x15\xcar\xb1\x06\x90\x0f\xe4\x1f\u0389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96b\xee\x02\x19&h+1\xc5\xf2\x00\xceEz\xbe\xa7ll\xe9\x89$Y\x0e\x85\x89\xebj\x00\x00\xe0\x94\x96l\x04x\x1c\xb5\xe6}\xde25\xd7\xf8b\x0e\x1a\xb6c\xa9\xa5\x8a\x10\r P\xdacQ`\x00\x00\u07d4\x96pv\xa8w\xb1\x8e\xc1ZA[\xb1\x16\xf0n\xf3&E\u06e3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96{\xfa\xf7bC\u0379@<g\xd2\xce\xef\xde\xe9\n?\xebs\x894\x9d\x87\xf2\xa2\xdc/\x00\x00\u07d4\x96}AB\xafw\x05\x15\xddpb\xaf\x93I\x8d\xbf\xdf\xf2\x9f \x89\x01\x18T\xd0\xf9\xce\xe4\x00\x00\u07d4\x96\x8b\x14d\x8f\x01\x833h|\xd2\x13\xfad\n\xec\x04\xcec#\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x96\x8d\xea`\xdf>\t\xae<\x8d5\x05\xe9\xc0\x80EK\xe0\xe8\x19\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x96\x92A\x91\xb7\xdfe[3\x19\xdcma7\xf4\x81\xa7:\x0f\xf3\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\x96\x96\x05!83\x8cr/\x11@\x81\\\xf7t\x9d\r;:t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x96\xa5_\x00\xdf\xf4\x05\xdcM\xe5\xe5\x8cW\xf6\xf6\xf0\xca\xc5]/\x89jf\x167\x9c\x87\xb5\x80\x00\u07d4\x96\xaaW?\xed/#4\x10\u06eeQ\x80\x14[#\xc3\x1a\x02\xf0\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x96\xadW\x9b\xbf\xa8\u06ce\xbe\xc9\u0486\xa7.Fa\xee\xd8\xe3V\x89:\v\xa4+\xeca\x83\x00\x00\u07d4\x96\xb44\xfe\x06W\xe4*\u0302\x12\xb6\x86Q9\xde\xde\x15\x97\x9c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xb9\x06\xear\x9fFU\xaf\xe3\xe5}5'|\x96}\xfa\x15w\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x96\xd6-\xfdF\b\u007fb@\x9d\x93\xdd`a\x88\xe7\x0e8\x12W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xd9\u0328\xf5^\xea\x00@\xecn\xb3H\xa1wK\x95\xd9>\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x96\xe7\xc0\xc9\u057f\x10\x82\x1b\xf1@\xc5X\xa1E\xb7\xca\xc2\x13\x97\x899>\xf1\xa5\x12|\x80\x00\x00\u07d4\x96\xeaj\u021a+\xac\x954{Q\u06e6=\x8b\xd5\xeb\xde\xdc\xe1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x96\xea\xfb\xf2\xfboM\xb9\xa46\xa7LE\xb5eDR\xe28\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x96\xebR>\x83/P\n\x01}\xe1>\xc2\u007f]6lV\x0e\xff\x89\x10\xac\u03baC\xee(\x00\x00\u07d4\x96\xf0F*\xe6\xf8\xb9`\x88\xf7\xe9\u018ct\xb9\u062d4\xb3G\x89a\t=|,m8\x00\x00\u07d4\x96\xf8 P\vp\xf4\xa3\xe3#\x9da\x9c\xff\x8f\" u\xb15\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x96\xfeY\xc3\u06f3\xaa|\xc8\xcbbH\fe\xe5nb\x04\xa7\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x96\xffoP\x99h\xf3l\xb4,\xbaH\xdb2\xf2\x1fVv\xab\xf8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x97\t8R*\xfb^\x8f\x99Hs\xc9\xfb\xdc&\xe3\xb3~1L\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\n\xbdS\xa5O\xcaJd) |\x18-MW\xbb9\u0520\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\r\x8b\x8a\x00\x16\xd1C\x05O\x14\x9f\xb3\xb8\xe5P\xdc\a\x97\u01c965\u026d\xc5\u07a0\x00\x00\u07d4\x97,/\x96\xaa\x00\u03ca/ Z\xbc\xf8\x93|\fu\xf5\xd8\u0649\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97?N6\x1f\xe5\xde\u0358\x9dL\x8f}|\xc9y\x908]\xaf\x89\x15\x0f\x85C\xa3\x87B\x00\x00\u07d4\x97M\x05A\xabJG\xec\u007fu6\x9c\x00i\xb6J\x1b\x81w\x10\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\x97M/\x17\x89_)\x02\x04\x9d\xea\xae\xcf\t\xc3\x04e\a@-\x88\xcc\x19\u00947\xab\x80\x00\u07d4\x97R\xd1O^\x10\x93\xf0qq\x1c\x1a\xdb\xc4\xe3\xeb\x1e\\W\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97V\xe1v\xc9\xefi>\xe1\xee\u01b9\xf8\xb1Q\xd3\x13\xbe\xb0\x99\x89A\rXj \xa4\xc0\x00\x00\u07d4\x97_7d\xe9{\xbc\xcfv|\xbd;y[\xa8m\x8b\xa9\x84\x0e\x89\x12\xc1\xb6\xee\xd0=(\x00\x00\xe0\x94\x97j\x18Sj\xf4\x18tBc\b\x87\x1b\xcd\x15\x12\xa7u\xc9\xf8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97n<\xea\xf3\xf1\xafQ\xf8\u009a\xff]\u007f\xa2\x1f\x03\x86\xd8\xee\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x97w\xcca\xcfuk\xe3\xb3\xc2\f\xd4I\x1ci\xd2u\xe7\xa1 \x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x97\x81\v\xaf\xc3~\x840c2\xaa\xcb5\xe9*\xd9\x11\xd2=$\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x97\x8cC\f\xe45\x9b\x06\xbc,\xdf\\)\x85\xfc\x95\x0eP\xd5\u0209\x1a\x05V\x90\xd9\u06c0\x00\x00\u07d4\x97\x95\xf6C\x19\xfc\x17\xdd\x0f\x82a\xf9\xd2\x06\xfbf\xb6L\xd0\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\x99\xca!\xdb\xcfi\xbf\xa1\xb3\xf7+\xacQ\xb9\xe3\xcaX|\xf9\x89\\(=A\x03\x94\x10\x00\x00\u07d4\x97\x9c\xbf!\xdf\xec\x8a\xce?\x1c\x19m\x82\u07d6%4\xdf9O\x89\x99\x91\xd4x\xddM\x16\x00\x00\u07d4\x97\x9dh\x1ca}\xa1o!\xbc\xac\xa1\x01\xed\x16\xed\x01Z\xb6\x96\x89e\xea=\xb7UF`\x00\x00\u07d4\x97\x9f0\x15\x8bWK\x99\x9a\xab4\x81\a\xb9\xee\xd8[\x1f\xf8\xc1\x894\x95tD\xb8@\xe8\x00\x00\u07d4\x97\xa8o\x01\xce?|\xfdDA3\x0e\x1c\x9b\x19\xe1\xb1\x06\x06\xef\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x97\xb9\x1e\xfesP\xc2\xd5~~@k\xab\x18\xf3a{\xcd\xe1J\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\x97\xd0\xd9r^;p\xe6u\x841s\x93\x8e\xd3q\xb6,\u007f\xac\x89\t79SM(h\x00\x00\u07d4\x97\xd9\xe4jv\x04\u05f5\xa4\xeaN\xe6\x1aB\xb3\xd25\x0f\xc3\xed\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x97\xdc&\xecg\n1\xe0\"\x1d*u\xbc]\xc9\xf9\f\x1fo\u0509\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\x97\xde!\xe4!\xc3\u007f\xe4\xb8\x02_\x9aQ\xb7\xb3\x90\xb5\xdfx\x04\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\x97\xe2\x89s\xb8`\xc5g@(\x00\xfb\xb6<\xe3\x9a\x04\x8a=y\x89\x05B%:\x12l\xe4\x00\x00\u07d4\x97\xe5\xcca'\xc4\xf8\x85\xbe\x02\xf4KB\xd1\u0230\xac\x91\u44c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x97\xf1\xfeL\x80\x83\xe5\x96!*\x18w(\xdd\\\xf8\n1\xbe\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\x97\xf7v\x06W\xc1\xe2\x02u\x90\x86\x96>\xb4!\x1c_\x819\xb9\x8a\n\x8a\t\u007f\xcb=\x17h\x00\x00\xe0\x94\x97\xf9\x9bk\xa3\x13F\u0358\xa9\xfeL0\x8f\x87\u0165\x8cQQ\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x98\n\x84\xb6\x86\xfc1\xbd\xc8<\"\x10XTjq\xb1\x1f\x83\x8a\x89*AUH\xaf\x86\x81\x80\x00\u07d4\x98\x10\xe3J\x94\xdbn\xd1V\xd08\x9a\x0e+\x80\xf4\xfdk\n\x8a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\x1d\xdf\x04\x04\xe4\xd2-\xdaUj\a&\xf0\v-\x98\xab\x95i\x8965f3\xeb\xd8\xea\x00\x00\xe0\x94\x98\x1fq'u\xc0\xda\xd9u\x18\xff\xed\xcbG\xb9\xad\x1dl'b\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\x984h!\x80\xb9\x82\xd1f\xba\u06dd\x9d\x1d\x9b\xbf\x01m\x87\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x986\xb4\xd3\x04sd\x1a\xb5j\xee\xe1\x92Bv\x1drrQx\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x989sB\xec_=L\xb8w\xe5N\xf5\xd6\xf1\xd3fs\x1b\u050a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\x98Fd\x886\xa3\a\xa0W\x18O\xd5\x1fb\x8a_\x8c\x12B|\x8a\x04\vi\xbfC\xdc\xe8\xf0\x00\x00\xe0\x94\x98Jy\x85\xe3\xcc~\xb5\xc96\x91\xf6\xf8\xcc{\x8f$]\x01\xb2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x98]p\xd2\a\x89+\xed9\x85\x90\x02N$!\xb1\xcc\x11\x93Y\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x98m\xf4~v\xe4\u05e7\x89\xcd\xee\x91<\u0243\x16P\x93l\x9d\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\x98t\x80?\xe1\xf3\xa06^y\"\xb1Bp\xea\xeb\x03,\xc1\xb5\x89<\xf5\x92\x88$\xc6\xc2\x00\x00\u07d4\x98ub4\x95\xa4l\xdb\xf2YS\x0f\xf88\xa1y\x9e\u00c9\x91\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98v\x18\xc8VV |{\xac\x15\a\xc0\xff\xef\xa2\xfbd\xb0\x92\x89\x03}\xfeC1\x89\xe3\x80\x00\u07d4\x98|\x9b\xcdn?9\x90\xa5+\xe3\xed\xa4q\f'Q\x8fOr\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x98\x82\x96|\xeeh\u04a89\xfa\u062bJ|=\xdd\xf6\xc0\xad\u0209Hx\xbe\x1f\xfa\xf9]\x00\x00\u07d4\x98\x85\\}\xfb\xee3SD\x90J\x12\xc4\fs\x17\x95\xb1:T\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4\x98\x9c\f\xcf\xf6T\xda\x03\xae\xb1\x1a\xf7\x01\x05Ea\xd6)~\x1d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xa0\xe5Lm\x9d\u023e\x96'l\xeb\xf4\xfe\xc4`\xf6#]\x85\x89j\u0202\x10\tR\u01c0\x00\u07d4\x98\xb7i\xcc0\\\xec\xfbb\x9a\x00\xc9\a\x06\x9d~\xf9\xbc:\x12\x89\x01h\u048e?\x00(\x00\x00\xe0\x94\x98\xbaN\x9c\xa7/\xdd\xc2\fi\xb49ov\xf8\x18?z*N\x8a\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\x00\u07d4\x98\xbeimQ\xe3\x90\xff\x1cP\x1b\x8a\x0fc1\xb6(\xdd\u016d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbe\u04e7.\xcc\xfb\xaf\xb9#H\x92\x93\xe4)\xe7\x03\xc7\xe2[\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x98\xbfJ\xf3\x81\v\x84#\x87\xdbp\xc1MF\t\x96&\x00=\x10\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x98\xc1\x0e\xbf,O\x97\u02e5\xa1\xab?*\xaf\xe1\xca\xc4#\xf8\u02c9\x10CV\x1a\x88)0\x00\x00\u07d4\x98\xc1\x9d\xba\x81\v\xa6\x11\xe6\x8f/\x83\xee\x16\xf6\xe7tO\f\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x98\xc5IJ\x03\xac\x91\xa7h\xdf\xfc\x0e\xa1\xdd\u0b3f\x88\x90\x19\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\x98\xd2\x04\xf9\b_\x8c\x8e}\xe2>X\x9bd\xc6\xef\xf6\x92\xccc\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x98\xd3s\x19\x92\xd1\xd4\x0e\x12\x11\xc7\xf75\xf2\x18\x9a\xfa\a\x02\xe0\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x98\xe2\xb6\xd6\x06\xfd-i\x91\xc9\xd6\xd4\a\u007f\xdf?\xddE\x85\u06890\xdf\x1ao\x8a\xd6(\x00\x00\u07d4\x98\xe3\xe9\v(\xfc\xca\ue087y\xb8\xd4\nUh\xc4\x11n!\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\x98\xe6\xf5G\u06c8\xe7_\x1f\x9c\x8a\xc2\xc5\xcf\x16'\xbaX\v>\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x98\xf4\xaf:\xf0\xae\xde_\xaf\xdcB\xa0\x81\xec\xc1\xf8\x9e<\xcf \x8a\x01\xfd\x934\x94\xaa_\xe0\x00\x00\u07d4\x98\xf6\xb8\xe6!=\xbc\x9aU\x81\xf4\xcc\xe6e_\x95%+\xdb\a\x89\x11Xr\xb0\xbc\xa40\x00\x00\u07d4\x99\te\r\u05719{\x8b\x8b\x0e\xb6\x94\x99\xb2\x91\xb0\xad\x12\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x99\x11s`\x19G\xc2\bJb\xd69R~\x96\x15\x12W\x9a\xf9\x89 \x86\xac5\x10R`\x00\x00\u07d4\x99\x12\x9d[<\f\xdeG\xea\r\xefM\xfc\a\r\x1fJY\x95'\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x99\x17\u058dJ\xf3A\xd6Q\xe7\xf0\a\\m\xe6\xd7\x14Nt\t\x8a\x012\xd4Gl\b\xe6\xf0\x00\x00\u07d4\x99\x1a\xc7\xcap\x97\x11_& ^\xee\x0e\xf7\xd4\x1e\xb4\xe3\x11\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\x99#e\xd7d\xc5\xce5@9\xdd\xfc\x91.\x02:u\xb8\xe1h\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x99&F\xac\x1a\u02ab\xf5\u076b\xa8\xf9B\x9a\xa6\xa9Nt\x96\xa7\x8967Pz0\xab\xeb\x00\x00\u07d4\x99&\x83'\xc3s3.\x06\xc3\xf6\x16B\x87\xd4U\xb9\xd5\xfaK\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99(\xffqZ\xfc:+`\xf8\xebL\u013aN\xe8\u06b6\u5749\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\x992\xef\x1c\x85\xb7Z\x9b*\x80\x05}P\x874\xc5\x10\x85\xbe\u0309\x02\xb8?\xa50\x1dY\x00\x00\xe0\x94\x99?\x14ax`^f\xd5\x17\xbex.\xf0\xb3\xc6\x1aN\x19%\x8a\x01|\x1f\x055\u05e5\x83\x00\x00\xe0\x94\x99A7\x04\xb1\xa3.p\xf3\xbc\ri\u0748\x1c8VkT\u02ca\x05\xcckiF1\xf7\x12\x00\x00\u07d4\x99AR\xfc\x95\xd5\xc1\u028b\x88\x11:\xbb\xadMq\x0e@\xde\xf6\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x99D\xfe\xe9\xd3JJ\x88\x00#\u01c92\xc0\vY\xd5\xc8*\x82\x89(\xa8\xa5k6\x90\a\x00\x00\u07d4\x99L\u00b5\"~\xc3\xcf\x04\x85\x12F|A\xb7\xb7\xb7H\x90\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99q\xdf`\xf0\xaef\xdc\xe9\xe8\xc8N\x17\x14\x9f\t\xf9\xc5/d\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x99v\x94~\xff_j\xe5\xda\b\xddT\x11\x92\xf3x\xb4(\xff\x94\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x99}e\x92\xa3\x15\x89\xac\xc3\x1b\x99\x01\xfb\xeb<\xc3\xd6[2\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x82\xa5\x89\x0f\xfbT\x06\u04ec\xa8\u04bf\xc1\xddp\xaa\xa8\n\xe0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\x87\x8f\x9dn\n~\u066e\u01c2\x97\xb78y\xa8\x01\x95\xaf\xe0\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\x99\x8c\x1f\x93\xbc\xdbo\xf2<\x10\xd0\u0712G(\xb7;\xe2\xff\x9f\x896[\xf3\xa43\xea\xf3\x00\x00\u07d4\x99\x91aL[\xaaG\xddl\x96\x87FE\xf9z\xdd,=\x83\x80\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x99\x92J\x98\x16\xbb}\xdf?\xec\x18D\x82\x8e\x9a\xd7\xd0k\xf4\xe6\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\x99\x99vh\xf7\xc1\xa4\xff\x9e1\xf9\x97z\xe3\"K\u02c8z\x85\x89\x0f\xc969(\x01\xc0\x00\x00\u07d4\x99\x9cI\xc1t\xca\x13\xbc\x83l\x1e\n\x92\xbf\xf4\x8b'\x15C\u0289\xb1\xcf$\xdd\u0431@\x00\x00\u07d4\x99\xa4\xde\x19\xde\u05d0\b\xcf\xdc\xd4]\x01M.XK\x89\x14\xa8\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\x99\xa9k\xf2$.\xa1\xb3\x9e\xceo\xcc\r\x18\xae\xd0\f\x01y\xf3\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x99\xb0\x18\x93+\xca\xd3U\xb6y+%]\xb6p-\xec\x8c\xe5\u0749\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\x99\xb7C\xd1\xd9\xef\xf9\r\x9a\x194\xb4\xdb!\xd5\x19\u061bJ8\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x99\xb8\xc8$\x86\x9d\xe9\xed$\xf3\xbf\xf6\x85L\xb6\xddE\xcc?\x9f\x89e\xea=\xb7UF`\x00\x00\u07d4\x99\xc0\x17L\xf8N\a\x83\xc2 \xb4\xebj\xe1\x8f\xe7\x03\x85J\u04c9py\xa2W=\fx\x00\x00\u07d4\x99\xc1\xd9\xf4\fj\xb7\xf8\xa9/\xce/\xdc\xe4zT\xa5\x86\xc5?\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x99\xc26\x14\x1d\xae\xc87\xec\xe0O\xda\xee\x1d\x90\u03cb\xbd\xc1\x04\x89ve\x16\xac\xac\r \x00\x00\u07d4\x99\xc3\x1f\xe7HX7\x87\xcd\xd3\xe5%\xb2\x81\xb2\x18\x96\x179\xe3\x897\b\xba\xed=h\x90\x00\x00\u07d4\x99\xc4u\xbf\x02\xe8\xb9!J\xda_\xad\x02\xfd\xfd\x15\xba6\\\f\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\x99\u0203%\x85F\xcc~N\x97\x1fR.8\x99\x18\xda^\xa6:\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x99\xc9\xf9>E\xfe<\x14\x18\xc3S\xe4\u016c8\x94\xee\xf8\x12\x1e\x89\x05\x85\xba\xf1E\x05\v\x00\x00\xe0\x94\x99\xd1W\x9c\xd4&\x82\xb7dN\x1dOq(D\x1e\xef\xfe3\x9d\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\x99\u0475\x85\x96_@jB\xa4\x9a\x1c\xa7\x0fv\x9evZ?\x98\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\x99\xdf\xd0PL\x06\xc7C\xe4e4\xfd{U\xf1\xf9\xc7\xec3)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xf4\x14|\xcck\u02c0\u0304.i\xf6\xd0\x0e0\xfaA3\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\x99\xf7\u007f\x99\x8b \xe0\xbc\xdc\xd9\xfc\x83\x86ARl\xf2Y\x18\xef\x89a\t=|,m8\x00\x00\u07d4\x99\xfa\xd5\x008\xd0\xd9\xd4\xc3\xfb\xb4\xbc\xe0V\x06\xec\xad\xcdQ!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x99\xfe\r \x12(\xa7S\x14VU\xd4(\xeb\x9f\xd9I\x85\xd3m\x89i \xbf\xf3QZ:\x00\x00\u07d4\x9a\a\x9c\x92\xa6)\xca\x15\xc8\xca\xfa.\xb2\x8d[\xc1z\xf8(\x11\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9a\r<\xee=\x98\x92\xea;7\x00\xa2\u007f\xf8A@\xd9\x02T\x93\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9a$\u038dH\\\xc4\xc8nI\u07b3\x90\"\xf9,t0\xe6~\x89Fy\x1f\xc8N\a\xd0\x00\x00\u07d4\x9a,\xe4;]\x89\u0593k\x8e\x8c5G\x91\xb8\xaf\xff\x96$%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9a9\x01bS^9\x88w\xe4\x16x}b9\xe0uN\x93|\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a=\xa6P#\xa10 \xd2!E\xcf\xc1\x8b\xab\x10\xbd\x19\xceN\x89\x18\xbfn\xa3FJ:\x00\x00\xe0\x94\x9a>+\x1b\xf3F\xdd\a\v\x02sW\xfe\xacD\xa4\xb2\xc9}\xb8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9aL\xa8\xb8!\x17\x89NC\xdbr\xb9\xfax\xf0\xb9\xb9:\xce\t\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9aR.R\xc1\x95\xbf\xb7\xcf_\xfa\xae\u06d1\xa3\xbath\x16\x1d\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9aZ\xf3\x1c~\x063\x9a\u0234b\x8d|M\xb0\xce\x0fE\u0224\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\x9ac?\xcd\x11,\xce\xebv_\xe0A\x81ps*\x97\x05\u7708\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9ac\u0445\xa7\x91)\xfd\xab\x19\xb5\x8b\xb61\xea6\xa4 TN\x89\x02F\xdd\xf9yvh\x00\x00\u07d4\x9ag\b\u0778\x90<(\x9f\x83\xfe\x88\x9c\x1e\xdc\xd6\x1f\x85D#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9ao\xf5\xf6\xa7\xaf{z\xe0\xed\x9c \xec\xecP#\u0481\xb7\x86\x89\x8a\x12\xb9\xbdjg\xec\x00\x00\xe0\x94\x9a\x82\x82m<)H\x1d\xcc+\u0495\x00G\xe8\xb6\x04\x86\xc38\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9a\x8e\xcaA\x89\xffJ\xa8\xff~\u0536\xb7\x03\x9f\t\x02!\x9b\x15\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9a\x95;[\xccp\x93y\xfc\xb5Y\u05f9\x16\xaf\u06a5\f\xad\u0309\x05k\xc7^-c\x10\x00\x00\u07d4\x9a\x99\v\x8a\xebX\x8d~\xe7\xec.\xd8\xc2\xe6Os\x82\xa9\xfe\xe2\x89\x01\xd1'\xdbi\xfd\x8b\x00\x00\u07d4\x9a\x9d\x1d\xc0\xba\xa7}n \xc3\xd8I\u01c8b\xdd\x1c\x05L\x87\x89/\xb4t\t\x8fg\xc0\x00\x00\xe0\x94\x9a\xa4\x8cf\xe4\xfbJ\u0419\x93N2\x02.\x82t'\xf2w\xba\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xa80\x8fB\x91\x0eZ\xde\t\xc1\xa5\xe2\x82\xd6\xd9\x17\x10\xbd\xbf\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9a\xaa\xfa\x00gd~\u0659\x06kzL\xa5\xb4\xb3\xf3\xfe\xaao\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9a\xb9\x88\xb5\x05\xcf\xee\x1d\xbe\x9c\u044e\x9bTs\xb9\xa2\xd4\xf56\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\x9a\xb9\x8dm\xbb\x1e\xaa\xe1mE\xa0EhT\x1a\xd3\xd8\xfe\x06\u0309\x0e\xc5\x04d\xfe#\xf3\x80\x00\xe0\x94\x9a\xba+^'\xffx\xba\xaa\xb5\xcd\u0248\xb7\xbe\x85\\\xeb\xbd\u038a\x02\x1e\f\x00\x13\a\n\xdc\x00\x00\u07d4\x9a\xc4\xdaQ\xd2x\"\xd1\xe2\b\xc9n\xa6J\x1e[U)\x97#\x89\x05lUy\xf7\"\x14\x00\x00\u0794\x9a\xc8S\x97y*i\u05cf(k\x86C*\a\xae\u03b6\x0ed\x88\xc6s\xce<@\x16\x00\x00\xe0\x94\x9a\xc9\a\xee\x85\xe6\xf3\xe2#E\x99\x92\xe2V\xa4?\xa0\x8f\xa8\xb2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9a\xd4\u007f\xdc\xf9\u0354-(\xef\xfd[\x84\x11[1\xa6X\xa1>\x89\xb2Y\xec\x00\xd5;(\x00\x00\u07d4\x9a\xdb\u04fc{\n\xfc\x05\xd1\xd2\xed\xa4\x9f\xf8c\x93\x9cH\xdbF\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9a\xdfE\x8b\xff5\x99\xee\xe1\xa2c\x98\x85<W[\u00ccc\x13\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x9a\xe1;\u0602\xf2Weu\x92\x1a\x94\x97L\xbe\xa8a\xba\r5\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\x9a\xe9Gk\xfe\xcd5\x91\x96M\xd3%\u03cc*$\xfa\xed\x82\xc1\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9a\xf1\x00\xcc=\xae\x83\xa34\x02\x05\x1c\xe4Ik\x16aT\x83\xf6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9a\xf1\x13\x99Q\x1c!1\x81\xbf\xda:\x8b&L\x05\xfc\x81\xb3\u038a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u0794\x9a\xf5\u0249L3\xe4,,Q\x8e:\xc6p\xea\x95\x05\u0475>\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\x9a\xf9\xdb\xe4t\"\xd1w\xf9E\xbd\xea\xd7\xe6\xd8)05b0\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9a\xfaSkLf\xbc8\xd8u\u0133\x00\x99\xd9&\x1f\xdb8\xeb\x89\v*\x8f\x84*w\xbc\x80\x00\u07d4\x9b\x06\xad\x84\x1d\xff\xbeL\xcfF\xf1\x03\x9f\u00c6\xf3\xc3!Dn\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\x11h\u078a\xb6KGU/3\x89\x80\n\x9c\xc0\x8bFf\u03c9]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9b\x18\x11\xc3\x05\x1fF\xe6d\xaeK\xc9\xc8$\u0445\x92\xc4WJ\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\x9b\x18G\x86U\xa4\x85\x1c\xc9\x06\xe6`\xfe\xaca\xf7\xf4\u023f\xfc\x89\xe2G\x8d8\x90}\x84\x00\x00\u07d4\x9b\"\xa8\r\\{3t\xa0[D`\x81\xf9}\n4\a\x9e\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9b+\xe7\xf5gT\xf5\x05\xe3D\x1a\x10\xf7\xf0\xe2\x0f\xd3\xdd\xf8I\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\x9b2\xcfOQ\x15\xf4\xb3J\x00\xa6La}\xe0c\x875C#\x89\x05\xb8\x1e\u0608 |\x80\x00\u07d4\x9bC\u0739_\xde1\x80u\xa5g\xf1\xe6\xb5v\x17\x05^\xf9\xe8\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\x9bDO\xd37\xe5\xd7R\x93\xad\xcf\xffp\xe1\xea\x01\xdb\x022\"\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bH$\xff\x9f\xb2\xab\xdaUM\xeeO\xb8\xcfT\x91eW\x061\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bL'\x15x\f\xa4\xe9\x9e`\xeb\xf2\x19\xf1Y\f\x8c\xadP\n\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\x9bY\xeb!;\x1eue\xe4PG\xe0N\xa07O\x10v-\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\\9\xf7\xe0\xac\x16\x8c\x8e\xd0\xed4\x04w\x11}\x1bh.\xe9\x89\x05P\x05\xf0\xc6\x14H\x00\x00\u07d4\x9b^\xc1\x8e\x83\x13\x88}\xf4a\u0490.\x81\xe6z\x8f\x11;\xb1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9bd\xd3\u034d+s\xf6hA\xb5\xc4k\xb6\x95\xb8\x8a\x9a\xb7]\x89\x01 :Ov\f\x16\x80\x00\u07d4\x9be\x8f\xb3a\xe0F\xd4\xfc\xaa\x8a\xefm\x02\xa9\x91\x11\"6%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9bfA\xb1>\x17/\xc0r\xcaK\x83'\xa3\xbc(\xa1[f\xa9\x89\x06\x81U\xa46v\xe0\x00\x00\xe0\x94\x9bh\xf6t\x16\xa6;\xf4E\x1a1\x16L\x92\xf6r\xa6\x87Y\xe9\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\x9bw6i\xe8}v\x01\x8c\t\x0f\x82U\xe5D\t\xb9\u0728\xb2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9bw\xeb\xce\xd7\xe2\x15\xf0\x92\x0e\x8c+\x87\x00$\xf6\xec\xb2\xff1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b|\x88\x10\xcc|\u021e\x80Nm>8\x12\x18PG(w\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9b\xa5=\xc8\xc9^\x9aG/\xeb\xa2\xc4\xe3,\x1d\xc4\xdd{\xabF\x89Hz\x9a0E9D\x00\x00\xe0\x94\x9b\xac\xd3\xd4\x0f;\x82\xac\x91\xa2d\xd9\u060d\x90\x8e\xac\x86d\xb9\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9b\xb7`\xd5\u0089\xa3\xe1\xdb\x18\xdb\tSE\xcaA;\x9aC\u0089\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\x9b\xb7b\x04\x18j\xf2\xf6;\xe7\x91h`\x16\x87\xfc\x9b\xadf\x1f\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9b\xb9\xb0*&\xbf\xe1\xcc\xc3\xf0\xc6!\x9e&\x1c9\u007f\xc5\xcax\x89Hz\x9a0E9D\x00\x00\u07d4\x9b\xc5s\xbc\xda#\xb8\xb2o\x90s\xd9\f#\x0e\x8eq\xe0'\v\x896/u\xa40]\f\x00\x00\u07d4\x9b\xd7\u00caB\x100JMe>\xde\xff\x1b<\xe4_\xcexC\x89\x0fI\x89A\xe6d(\x00\x00\xe0\x94\x9b\u0600h\xe10u\xf3\xa8\xca\xc4d\xa5\xf9I\xd6\xd8\x18\xc0\xf6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9b\xd9\x05\xf1q\x9f\u01ec\xd0\x15\x9dM\xc1\xf8\xdb/!G#8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9b\xdb\u071b\x9741\xd1<\x89\xa3\xf9u~\x9b;bu\xbf\u01c9\x1b\x1a}\u03caD\u04c0\x00\u07d4\x9b\xe3\xc3)\xb6*(\xb8\xb0\x88l\xbd\x8b\x99\xf8\xbc\x93\f\xe3\xe6\x89\x04\t\xe5+H6\x9a\x00\x00\xe0\x94\x9b\xf5\x8e\xfb\xea\a\x84\xeb\x06\x8a\xde\u03e0\xbb!P\x84\xc7:5\x8a\x01:k+VHq\xa0\x00\x00\u07d4\x9b\xf6r\xd9y\xb3fR\xfcR\x82Tzjk\xc2\x12\xaeCh\x89#\x8f\xd4,\\\xf0@\x00\x00\xe0\x94\x9b\xf7\x03\xb4\x1c6$\xe1_@T\x96#\x90\xbc\xba0R\xf0\xfd\x8a\x01H>\x01S<.<\x00\x00\u07d4\x9b\xf7\x1f\u007f\xb57\xacT\xf4\xe5\x14\x94\u007f\xa7\xffg(\xf1m/\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\x9b\xf9\xb3\xb2\xf2<\xf4a\xebY\x1f(4\v\xc7\x19\x93\x1c\x83d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9b\xfce\x9c\x9c`\x1e\xa4*k!\xb8\xf1p\x84\xec\x87\xd7\x02\x12\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9b\xff\xf5\r\xb3jxUU\xf0vR\xa1S\xb0\xc4+\x1b\x8bv\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\x05\xe9\xd0\xf0u\x8eyS\x03q~1\xda!<\xa1W\u618965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\x1bw\x1f\t\xaf\x88*\xf0d0\x83\xde*\xa7\x9d\xc0\x97\xc4\x0e\x89\x86p\xe9\xece\x98\xc0\x00\x00\u07d4\x9c(\xa2\xc4\b`\x91\xcb]\xa2&\xa6W\xce2H\xe8\xea{o\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\x9c/\xd5@\x89\xaff]\xf5\x97\x1ds\xb8\x04a`9dsu\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c4@\x98\xbaaZ9\x8f\x11\xd0\t\x90[\x17|D\xa7\xb6\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c=\x06\x92\xce\xee\xf8\n\xa4\x96\\\xee\xd2b\xff\xc7\xf0i\xf2\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c@\\\xf6\x97\x95a8\x06^\x11\xc5\xf7U\x9eg$[\u0465\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cE *%\xf6\xad\x00\x11\xf1\x15\xa5\xa7\"\x04\xf2\xf2\x19\x88f\x8a\x01\x0f\xcf:b\xb0\x80\x98\x00\x00\xe0\x94\x9cI\xde\xffG\b_\xc0\x97\x04\u02a2\u0728\u0087\xa9\xa17\u068a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\x9cK\xbc\xd5\xf1dJo\aX$\xdd\xfe\x85\xc5q\u05ab\xf6\x9c\x89a\x94\x04\x9f0\xf7 \x00\x00\u07d4\x9cRj\x14\x06\x83\xed\xf1C\x1c\xfa\xa1(\xa95\xe2\xb6\x14\u060b\x89\x06\x04o7\xe5\x94\\\x00\x00\xe0\x94\x9cT\xe4\xedG\x9a\x85h)\u01bbB\u069f\vi*u\xf7(\x8a\x01\x97\xa8\xf6\xddU\x19\x80\x00\x00\xe0\x94\x9cX\x1a`\xb6\x10(\xd94\x16y)\xb2-p\xb3\x13\xc3O\u040a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\x9c\\\xc1\x11\t,\x12!\x16\xf1\xa8_N\xe3\x14\bt\x1a}/\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\x9ck\u0264k\x03\xaeT\x04\xf0C\xdf\xcf!\x88>A\x10\xcc3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9cx\x96?\xbc&<\t\xbdr\xe4\xf8\xde\xf7J\x94u\xf7\x05\\\x8a\x02\ub3b1\xa1r\u0738\x00\x00\u07d4\x9cx\xfb\xb4\xdfv\x9c\xe2\xc1V\x92\f\xfe\xdf\xda\x03:\x0e%J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9c{m\xc5\x19\x0f\xe2\x91)c\xfc\xd5yh>\xc79Q\x16\xb0\x89*\x11)\u0413g \x00\x00\u07d4\x9c\x80\xbc\x18\xe9\xf8\u0516\x8b\x18]\xa8\u01df\xa6\xe1\x1f\xfc>#\x89\r\x02\xabHl\xed\xc0\x00\x00\xe0\x94\x9c\x98\xfd\xf1\xfd\u034b\xa8\xf4\u0170L:\xe8X~\xfd\xf0\xf6\xe6\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\x9c\x99\xa1\u0691\u0552\v\xc1N\f\xb9\x14\xfd\xf6+\x94\u02c3X\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\x9c\x99\xb6&\x06(\x1b\\\xef\xab\xf3aV\xc8\xfeb\x83\x9e\xf5\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9c\x9a\a\xa8\xe5|1r\xa9\x19\xefdx\x94tI\x0f\r\x9fQ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\x9d\xe4G$\xa4\x05M\xa0\xea\xa6\x05\xab\u0300&hw\x8b\xea\x89\n\xd7\xd5\xca?\xa5\xa2\x00\x00\u07d4\x9c\x9f;\x8a\x81\x1b!\xf3\xff?\xe2\x0f\xe9p\x05\x1c\xe6j\x82O\x89>\xc2\u07bc\a\u053e\x00\x00\xe0\x94\x9c\x9f\x89\xa3\x91\x0fj*\xe8\xa9\x10G\xa1z\xb7\x88\xbd\xde\xc1p\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9c\xa0B\x9f\x87O\x8d\xce\xe2\xe9\xc0b\xa9\x02\n\x84*Xz\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xa4.\u7838\x98\xf6\xa5\xcc`\xb5\xa5\u05f1\xbf\xa3\xc321\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9c\xb2\x8a\xc1\xa2\n\x10o\u007f76\x92\xc5\xceLs\xf172\xa1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9c\xcd\u0732\xcf\u00b2[\br\x9a\n\x98\xd9\xe6\xf0 .\xa2\xc1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9c\xe2\u007f$^\x02\xd1\xc3\x12\xc1\xd5\x00x\x8c\x9d\xefv\x90E;\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9c\xe56;\x13\xe8#\x8a\xa4\xdd\x15\xac\u0432\xe8\xaf\xe0\x872G\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\x9c\xf2\x92\x8b\xee\xf0\x9a@\xf9\xbf\xc9S\xbe\x06\xa2Q\x11a\x82\xfb\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9d\x06\x91\x97\xd1\xdeP\x04Z\x18o^\xc7D\xac@\u8bd1\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d\x0e}\x92\xfb0XS\u05d8&;\xf1^\x97\xc7+\xf9\xd7\xe0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9d\x0f4~\x82k}\u03aa\xd2y\x06\n5\xc0\x06\x1e\xcf3K\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d u\x17B,\xc0\xd6\r\xe7\xc27\tzMO\xce \x94\f\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\x9d%\n\xe4\xf1\x10\xd7\x1c\xaf\u01f0\xad\xb5.\x8d\x9a\xcbfy\xb8\x8a\x02\x15mn\x99r\x13\xc0\x00\x00\xe0\x94\x9d+\xfc6\x10o\x03\x82P\xc0\x18\x01hW\x85\xb1l\x86\xc6\r\x8aPw\xd7]\xf1\xb6u\x80\x00\x00\xe0\x94\x9d0\xcb#{\xc0\x96\xf1p6\xfc\x80\xdd!\xcah\x99,\xa2\u064a\x06n\xe71\x8f\u070f0\x00\x00\u07d4\x9d2\x96.\xa9\x97\x00\xd92(\xe9\xdb\xda\xd2\xcc7\xbb\x99\xf0~\x89\xb4c+\xed\xd4\xde\xd4\x00\x00\u07d4\x9d4\xda\xc2[\xd1X(\xfa\xef\xaa\xf2\x8fq\aS\xb3\x9e\x89\u0709;\x1cV\xfe\xd0-\xf0\x00\x00\u07d4\x9d6\x91e\xfbp\xb8\x1a:v_\x18\x8f\xd6\f\xbe^{\th\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9d@\xe0\x12\xf6\x04%\xa3@\xd8-\x03\xa1\xc7W\xbf\xab\xc7\x06\xfb\x89\t4o:\xdd\u020d\x80\x00\u07d4\x9dAt\xaaj\xf2\x84v\xe2)\xda\xdbF\x18\b\b\xc6u\x05\xc1\x89B\x1a\xfd\xa4.\u0597\x00\x00\u07d4\x9dB\x133\x9a\x01U\x18avL\x87\xa9<\xe8\xf8_\x87\x95\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9dF\f\x1b7\x9d\xdb\x19\xa8\xc8[LgG\x05\r\xdf\x17\xa8u\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\x9dG\xba[L\x85\x05\xad\x8d\xa4)4(\va\xa0\xe1\xe8\xb9q\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9dM2\x11w%n\xbd\x9a\xfb\xda0A5\xd5\x17\xc3\xdcV\x93\x89!d\xb7\xa0J\u0220\x00\x00\u07d4\x9dO\xf9\x89\xb7\xbe\u066b\x10\x9d\x10\xc8\xc7\xe5_\x02\xd7g4\xad\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\x9dQ\x15C\xb3\xd9\xdc`\xd4\u007f\t\u051d\x01\xb6\u0118\xd8 x\x8a\x02a\x97\xb9Qo\u00d4\x00\x00\u07d4\x9dn\u03e0:\xf2\xc6\xe1D\xb7\xc4i*\x86\x95\x1e\x90.\x9e\x1f\x89\xa2\xa5\xaa`\xad$?\x00\x00\u07d4\x9dvU\xe9\xf3\xe5\xba]n\x87\xe4\x12\xae\xbe\x9e\xe0\u0512G\ue24e\t1\x1c\x1d\x80\xfa\x00\x00\u07d4\x9dx1\xe84\xc2\v\x1b\xaaiz\xf1\xd8\xe0\xc6!\u016f\xff\x9a\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\x9dx\xa9u\xb7\xdb^M\x8e(\x84\\\xfb\xe7\xe3\x14\x01\xbe\r\u0649H\xa4<T`/p\x00\x00\u07d4\x9dy\x9e\x94>0k\xa2\u5e5c\x8ahX\u02f5,\f\xf75\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\x9d\u007f\xdapp\xbf>\xe9\xbb\u0664\x1fU\xca\u0505J\xe6\xc2,\x8a\x02U\u02e3\xc4o\xcf\x12\x00\x00\u07d4\x9d\x81\xae\xa6\x9a\xedj\xd0p\x89\xd6\x14E4\x8c\x17\xf3K\xfc[\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9d\x91\x1f6\x82\xf3/\xe0y.\x9f\xb6\xff<\xfcG\xf5\x89\xfc\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\x91;]3\x9c\x95\xd8wEV%c\xfe\xa9\x8b#\xc6\f\u0109\tA0,\u007fM#\x00\x00\u07d4\x9d\x93\xfa\xb6\xe2(E\xf8\xf4Z\aIo\x11\xdeqS\r\xeb\u01c9lO\xd1\xee$nx\x00\x00\u07d4\x9d\x99\xb1\x89\xbb\u0664\x8f\xc2\xe1n\x8f\u0363;\xb9\x9a1{\xbb\x89=\x16\xe1\vm\x8b\xb2\x00\x00\u07d4\x9d\x9cN\xfe\x9fC9\x89\xe2;\xe9@I!S)\xfaU\xb4\u02c9\r\u3c89\x03\u01b5\x80\x00\u07d4\x9d\x9eW\xfd\xe3\x0ePh\xc0>I\x84\x8e\xdc\xe3C\xb7\x02\x83X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\x9d\xa30\"@\xaf\x05\x11\xc6\xfd\x18W\xe6\u07779Ow\xabk\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\x9d\xa4\xec@pw\xf4\xb9p{-\x9d.\xde^\xa5(+\xf1\u07c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xa6\t\xfa:~l\xf2\xcc\x0ep\u036b\xe7\x8d\xc4\xe3\x82\xe1\x1e\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\x9d\xa6\x1c\xcdb\xbf\x86\x06V\xe02]qW\xe2\xf1`\xd9;\xb5\x8a\x01\x0f\f\xa9V\xf8y\x9e\x00\x00\xe0\x94\x9d\xa6\xe0u\x98\x9ct\x19\tL\xc9\xf6\xd2\u44d3\xbb\x19\x96\x88\x8a\x02Y\xbbq\u056d\xf3\xf0\x00\x00\u07d4\x9d\xa8\xe2,\xa1\x0eg\xfe\xa4NR^GQ\xee\xac6\xa3\x11\x94\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\x9d\xb2\xe1\\\xa6\x81\xf4\xc6`H\xf6\xf9\xb7\x94\x1e\u040b\x1f\xf5\x06\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9d\xc1\x0f\xa3\x8f\x9f\xb0h\x10\xe1\x1f`\x17>\xc3\xd2\xfdju\x1e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\x9d\xd2\x19f$\xa1\xdd\xf1J\x9d7^_\a\x15+\xaf\"\xaf\xa2\x89A\xb0^$c\xa5C\x80\x00\u07d4\x9d\xd4k\x1cm?\x05\u279co\x03~\xed\x9aYZ\xf4\xa9\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9d\xdd5^cN\xe9\x92~K\u007fl\x97\xe7\xbf:/\x1ehz\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\x9d\xe2\n\xe7j\xa0\x82c\xb2\x05\xd5\x14$a\x96\x1e$\b\xd2f\x89\r\xa93\xd8\xd8\xc6p\x00\x00\u07d4\x9d\xe2\v\xc3~\u007fH\xa8\x0f\xfdz\xd8O\xfb\xf1\xa1\xab\xe1s\x8c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9d\xe78m\xde@\x1c\xe4\xc6{q\xb6U?\x8a\xa3N\xa5\xa1}\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\x9d\xeb9\x02z\xf8w\x99+\x89\xf2\xecJ\x1f\x82.\xcd\xf1&\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xef\xe5j\x0f\xf1\xa1\x94}\xba\t#\xf7\xdd%\x8d\x8f\x12\xfaE\x8a\x05\xb1*\ufbe8\x04\x00\x00\x00\u07d4\x9d\xf0W\xcd\x03\xa4\xe2~\x8e\x03/\x85y\x85\xfd\u007f\x01\xad\xc8\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9d\xf3*P\x1c\vx\x1c\x02\x81\x02/B\xa1)?\xfd{\x89*\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\x9e\x01vZ\xff\b\xbc\"\x05P\xac\xa5\xea.\x1c\xe8\u5c19#\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9e \xe5\xfd6\x1e\xab\xcfc\x89\x1f[\x87\xb0\x92h\xb8\xeb7\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e#,\b\xc1M\xc1\xa6\xed\v\x8a;(h\x97{\xa5\xc1}\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e#\xc5\u4dc2\xb0\n_\xad\U0006eb47\xda\xcf[\x03g\xa1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e59\x90q\xa4\xa1\x01\xe9\x19M\xaa?\t\xf0J\v_\x98p\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e>\xb5\t'\x8f\xe0\xdc\xd8\xe0\xbb\xe7\x8a\x19N\x06\xb6\x809C\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\x9eBrrQk>g\xd4\xfc\xbf\x82\xf5\x93\x90\xd0L\x8e(\xe5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\x9eL\xec5:\xc3\u3043^<\t\x91\xf8\xfa\xa5\xb7\u0428\xe6\x8a\x02\x1e\x18\xb9\xe9\xabE\xe4\x80\x00\u07d4\x9eX\x11\xb4\v\xe1\xe2\xa1\xe1\u048c;\at\xac\xde\n\t`=\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\x9eZ1\x1d\x9fi\x89\x8a|j\x9dc`h\x048\xe6z{/\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\x9e| P\xa2'\xbb\xfd`\x93~&\x8c\xea>h\xfe\xa8\xd1\xfe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\x9e\u007fe\xa9\x0e\x85\b\x86{\xcc\xc9\x14%j\x1e\xa5t\xcf\a\xe3\x89C8t\xf62\xcc`\x00\x00\xe0\x94\x9e\x81D\xe0\x8e\x89dx\x11\xfekr\xd4E\u05a5\xf8\n\xd2D\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9e\x8fd\xdd\xcd\u9e34Q\xba\xfa\xa25\xa9\xbfQ\x1a%\xac\x91\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\x9e\x95\x1fm\xc5\xe3R\xaf\xb8\xd0B\x99\xd2G\x8aE\x12Y\xbfV\x89\x03\xe7A\x98\x81\xa7:\x00\x00\u07d4\x9e\x96\r\xcd\x03\u057a\x99\xcb\x11]\x17\xffL\t$\x8a\xd4\u043e\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9e\xafj2\x8a@v\x02N\xfakg\xb4\x8b!\xee\xdc\xc0\xf0\xb8\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\x9e\xb1\xffqy\x8f(\xd6\xe9\x89\xfa\x1e\xa0X\x8e'\xba\x86\xcb}\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\x9e\xb2\x81\xc3'\x19\xc4\x0f\xdb>!m\xb0\xf3\u007f\xbcs\xa0&\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9e\xb3\xa7\xcb^g&Bz:6\x1c\xfa\x8dad\xdb\u043a\x16\x89+\x95\xbd\xcc9\xb6\x10\x00\x00\u07d4\x9e\xb7\x83N\x17\x1dA\xe0i\xa7yG\xfc\xa8v\"\xf0\xbaNH\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\x9e\xc0>\x02\u51f7v\x9d\xefS\x84\x13\xe9\u007f~U\xbeq\u060a\x04+\xf0kx\xed;P\x00\x00\u07d4\x9e\u02eb\xb0\xb2'\x82\xb3uD)\xe1uz\xab\xa0K\x81\x18\x9f\x89,\xa7\xbb\x06\x1f^\x99\x80\x00\u07d4\x9e\xce\x14\x00\x80\t6\xc7\xc6H_\xcd\xd3b`\x17\u041a\xfb\xf6\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\x9e\xd4\xe6?ReB\xd4O\xdd\xd3MY\xcd%8\x8f\xfdk\u0689\u049b4\xa4cH\x94\x00\x00\u07d4\x9e\xd8\x0e\xda\u007fU\x05M\xb9\xfbR\x82E\x16\x88\xf2k\xb3t\xc1\x89\x10CV\x1a\x88)0\x00\x00\u07d4\x9e\u0710\xf4\xbe!\be!J\xb5\xb3^Z\x8d\xd7t\x15'\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9e\u07acL\x02k\x93\x05M\u0171\xd6a\fo9`\xf2\xads\x89A\rXj \xa4\xc0\x00\x00\u07d4\x9e\xe9?3\x9eg&\xece\xee\xa4O\x8aK\xfe\x10\xda=2\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\x9e\xe9v\f\xc2s\xd4pj\xa0\x83u\xc3\xe4o\xa20\xaf\xf3\u054a\x01\xe5.3l\xde\"\x18\x00\x00\u07d4\x9e\xeb\a\xbd+x\x90\x19^}F\xbd\xf2\a\x1bf\x17QM\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\x9e\xefD-)\x1aD}t\xc5\xd2S\u011e\xf3$\xea\xc1\xd8\xf0\x89\xb9f\b\xc8\x10;\xf0\x00\x00\u07d4\x9e\xf1\x89k\x00|2\xa1Q\x14\xfb\x89\xd7=\xbdG\xf9\x12+i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9f\x01w\x06\xb80\xfb\x9c0\ufc20\x9fPk\x91WEu4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x10\xf2\xa0F;e\xae0\xb0p\xb3\xdf\x18\xcfF\xf5\x1e\x89\xbd\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\x9f\x19\xfa\u0223$7\xd8\n\u0183z\v\xb7\x84\x17)\xf4\x97.\x89#=\xf3)\x9far\x00\x00\u07d4\x9f\x1a\xa8\xfc\xfc\x89\xa1\xa52\x8c\xbdcD\xb7\x1f'\x8a,\xa4\xa0\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\x9f!0,\xa5\tk\xeat\x02\xb9\x1b\x0f\xd5\x06%O\x99\x9a=\x89C\x97E\x1a\x00=\xd8\x00\x00\u07d4\x9f'\x1d(U\x00\xd78F\xb1\x8fs>%\u074bO]J\x8b\x89'#\xc3F\xae\x18\b\x00\x00\u07d4\x9f4\x97\xf5\xef_\xe60\x95\x83l\x00N\xb9\xce\x02\xe9\x01;K\x89\"V\x86\x1b\xf9\xcf\b\x00\x00\xe0\x94\x9f:t\xfd^~\xdc\xc1\x16)\x93\x17\x13\x81\u02f62\xb7\xcf\xf0\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\x9fF\xe7\xc1\xe9\a\x8c\xae\x860Z\xc7\x06\v\x01F}f\x85\xee\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\x9fIl\xb2\x06\x95c\x14M\b\x11g{\xa0\xe4q:\nAC\x89<\xd2\xe0\xbfc\xa4H\x00\x00\u07d4\x9fJq\x95\xac|\x15\x1c\xa2X\xca\xfd\xa0\u02b0\x83\xe0I\xc6\x02\x89SS\x8c2\x18\\\xee\x00\x00\u07d4\x9fJ\xc9\xc9\xe7\xe2L\xb2DJ\x04T\xfa[\x9a\xd9\xd9-8S\x89-C\xf3\xeb\xfa\xfb,\x00\x00\u07d4\x9f_D\x02kWjJ\xdbA\xe9YaV\x1dA\x03\x9c\xa3\x91\x89\r\x8drkqw\xa8\x00\x00\u07d4\x9f`{?\x12F\x9fDa!\u03bf4u5kq\xb42\x8c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\x9fa\xbe\xb4o^\x85=\n\x85!\xc7Dnh\xe3L}\ts\x89\x1e[\x8f\xa8\xfe*\xc0\x00\x00\u07d4\x9fd\xa8\xe8\xda\xcfJ\xde0\xd1\x0fMY\xb0\xa3\u056b\xfd\xbft\x8966\x9e\xd7t}&\x00\x00\u07d4\x9ff.\x95'A!\xf1wVncm#\x96L\xf1\xfdho\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9fj2*mF\x99\x81Bj\xe8D\x86]~\xe0\xbb\x15\u01f3\x89\x02\xb5\xeeW\x92\x9f\u06c0\x00\u07d4\x9fy\x86\x92J\xeb\x02h|\xd6A\x89\x18\x9f\xb1g\xde\xd2\xdd\\\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\x9fz\x03\x92\xf8Ws.0\x04\xa3u\xe6\xb1\x06\x8dI\xd801\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x82E\u00eb}\x171d\x86\x1c\u04d9\x1b\x94\xf1\xba@\xa9:\x89\x9b\ny\x1f\x12\x110\x00\x00\u07d4\x9f\x83\xa2\x93\xc3$\xd4\x10l\x18\xfa\xa8\x88\x8fd\u0499\x05L\xa0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\x9f\x86\xa0f\xed\xb6\x1f\xcbXV\u0793\xb7\\\x8cy\x18d\xb9{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\x98\xeb4\xd4iy\xb0\xa6\u078b\x05\xaaS:\x89\xb8%\xdc\xf1\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\xe0\x94\x9f\x9f\xe0\xc9_\x10\xfe\xe8z\xf1\xaf r6\xc8\xf3aN\xf0/\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\x9f\xae\xa1<s4\x12\xdcKI\x04\x02\xbf\xef'\xa09z\x9b\u00c9\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\x9f\xbe\x06m\xe5r6\u0703\a%\xd3*\x02\xae\xf9$ll^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\x9f\xd1\x05*`Pk\u0469\xef\x00:\xfd\x9d\x03<&}\x8e\x99\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9f\xd6Cs\xf2\xfb\u035c\x0f\xac\xa6\x05G\xca\xd6.&\u0645\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9f\xe5\x01\xaaW\xea\u05d2x\x93|\xd60\x8c\\\xfazV)\xfe\x89\x02\xb5\xeeW\x92\x9f\u06c0\x00\u07d4\x9f\xfc_\xe0o3\xf5\xa4\x80\xb7Z\xa9N\xb8Um\x99z\x16\xc0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\x9f\xfc\xf5\xefF\xd93\xa5\x19\xd1\xd1lk\xa3\x18\x9b'Ib$\x8965\u026d\xc5\u07a0\x00\x00\u07d4\x9f\xfe\xdc\xc3k|\xc3\x12\xad*\x9e\xdeC\x1aQO\u0334\x9b\xa3\x89$OW\x9f?\\\xa4\x00\x00\u07d4\xa0\x06&\x84Fd>\xc5\xe8\x1ez\xcb?\x17\xf1\xc3Q\xee.\u0649\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0\b\x01\x98c\xc1\xa7|\x14\x99\xeb9\xbb\u05ff-\u05e3\x1c\xb9\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xa0\t\xbf\ao\x1b\xa3\xfaW\u04a7!r\x18\xbe\xd5VZzz\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0\x1e\x94v\u07c4C\x18%\xc86\xe8\x80:\x97\xe2/\xa5\xa0\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xa0\x1f\x12\xd7\x0fD\xaa{\x11;(\\\"\xdc\xdbE\x874T\xa7\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa0\x1f\u0450j\x90\x85\x06\xde\xda\xe1\xe2\b\x12\x88r\xb5n\u7489\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa0\"\x82@\xf9\x9e\x1d\xe9\xcb2\xd8,\x0f/\xa9\xa3\xd4K\v\xf3\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xa0+\xdedahn\x19\xace\f\x97\r\x06r\xe7m\xcbO\u008a\x01\xe0\x92\x96\xc37\x8d\xe4\x00\x00\u07d4\xa0,\x1e4\x06O\x04u\xf7\xfa\x83\x1c\xcb%\x01L:\xa3\x1c\xa2\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xa0-\u01aa2\x8b\x88\r\u97acTh#\xfc\xcfw@G\xfb\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa0.?\x8fYY\xa7\xaa\xb7A\x86\x12\x12\x9bp\x1c\xa1\xb8\x00\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa04\u007f\n\x98wc\x90\x16\\\x16m2\x96;\xf7M\xcd\n/\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa05\xa3e$x\xf8-\xbdm\x11_\xaa\x8c\xa9F\xec\x9eh\x1d\x89\x05\xf4\xe4-\u052f\xec\x00\x00\u07d4\xa0:=\xc7\xc53\xd1tB\x95\xbe\x95]a\xaf?R\xb5\x1a\xf5\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa0E\x9e\xf3i:\xac\xd1d|\xd5\u0612\x989 L\xefS\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa0O*\xe0*\xdd\x14\xc1/\xafe\xcb%\x90\"\u0403\n\x8e&\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xa0l\xd1\xf3\x969l\ndFFQ\xd7\xc2\x05\xef\xaf8|\xa3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa0ri\x1c\x8d\xd7\xcdB7\xffr\xa7\\\x1a\x95\x06\xd0\xce[\x9e\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xa0r\u03beb\xa9\xe9\xf6\x1c\xc3\xfb\xf8\x8a\x9e\xfb\xfe>\x9a\x8dp\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0v\x82\x00\v\x1b\xcf0\x02\xf8\\\x80\xc0\xfa)I\xbd\x1e\x82\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa0z\xa1mt\xae\u8a63(\x8dR\xdb\x15Q\u0553\x882\x97\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x8d![[j\xacHa\xa2\x81\xac~@\vx\xfe\xf0L\xbf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa0\x95\x19p\xdf\u0403/\xb8;\xda\x12\xc25E\xe7\x90Aul\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa0\x9fM^\xaae\xa2\xf4\xcbu\nI\x924\x01\xda\u5410\xaf\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\xe0\x94\xa0\xa0\xe6R\x04T\x1f\u029b/\xb2\x82\u0355\x13\x8f\xae\x16\xf8\t\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa0\xaa_\x02\x01\xf0M;\xbe\xb8\x98\x13/|\x11g\x94f\xd9\x01\x89\x01\xfb\xedR\x15\xbbL\x00\x00\u07d4\xa0\xaa\xdb\xd9P\x97\"p_m#X\xa5\u01df7\x97\x0f\x00\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa0\xb7q\x95\x1c\xe1\xde\xee6:\xe2\xb7q\xb7>\a\u0135\xe8\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa0\xde\\`\x1eif5\u0198\xb7\xae\x9c\xa4S\x9f\u01f9A\xec\x89\x12\xc3\xcb\xd7\x04\xc9w\x00\x00\u07d4\xa0\xe8\xbaf\x1bH\x15L\xf8C\xd4\u00a5\xc0\xf7\x92\xd5(\xee)\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa0\xfc~S\xc5\xeb\xd2z*\xbd\xacE&\x1f\x84\xab;Q\xae\xfb\x89\xa3\x13\xda\xec\x9b\xc0\xd9\x00\x00\xe0\x94\xa0\xff[L\xf0\x16\x02~\x83#I}D(\xd3\xe5\xa8;\x87\x95\x8a\x01e\x98\xd3\xc8>\xc0B\x00\x00\u07d4\xa1\x06F[\xbd\x19\u1dbc\xe5\r\x1b\x11W\xdcY\tZ60\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa1\x06\xe6\x92>\xddS\u028e\xd6P\x96\x8a\x91\b\xd6\xcc\xfd\x96p\x8a\x02\x02\xfe\x15\x05\xaf\uc240\x00\u07d4\xa1\t\u12f0\xa3\x9c\x9e\xf8/\xa1\x95\x97\xfc^\xd8\xe9\xebmX\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xa1\x1a\x03\u013b&\xd2\x1e\xffg}]U\\\x80\xb2TS\xeez\x89\x03\xcb'Y\xbcA\x0f\x80\x00\u07d4\xa1\x1e\xff\xabl\xf0\xf5\x97,\xff\xe4\xd5e\x96\xe9\x89h\x14J\x8f\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xa1 M\xad_V\a(\xa3\\\r\x8f\u01d4\x81\x05{\xf7s\x86\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa1&#\xe6)\u07d3\tg\x04\xb1`\x84\xbe,\u061dV-\xa4\x8a\x01\xcc\xc92E\x11\xe4P\x00\x00\xe0\x94\xa1*l-\x98]\xaf\x0eO_ z\xe8Q\xaa\xf7)\xb32\u034a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xa13m\xfb\x96\xb6\xbc\xbeK>\xdf2\x05\xbeW#\xc9\x0f\xadR\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa1;\x9d\x82\xa9\x9b<\x9b\xbaZ\xe7.\xf2\x19\x9e\xdc};\xb3l\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa1<\xfe\x82mm\x18A\u072eD;\xe8\u00c7Q\x816\xb5\xe8\x8a\x1d\xa5jK\b5\xbf\x80\x00\x00\xe0\x94\xa1C.\xd2\u01b7wz\x88\xe8\xd4m8\x8epG\u007f \x8c\xa5\x8a\x01\xb1\xa7\xe4\x13\xa1\x96\xc5\x00\x00\u07d4\xa1D\xf6\xb6\x0fr\xd6J!\xe30\xda\xdbb\u0619\n\xde+\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1P%\xf5\x95\xac\xdb\xf3\x11\x0fw\u017f$G~eH\xf9\xe8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1X\x14\x8a.\x0f>\x92\xdc,\xe3\x8f\xeb\xc2\x01\a\xe3%<\x96\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1a`\x85\x1d+\x9c4\x9b\x92\xe4o\x82\x9a\xbf\xb2\x10\x945\x95\x89a\t=|,m8\x00\x00\u07d4\xa1f\xf9\x11\xc6D\xac2\x13\u049e\x0e\x1a\xe0\x10\xf7\x94\u056d&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1m\x9e=c\x98aY\xa8\x00\xb4h7\xf4^\x8b\xb9\x80\xee\v\x89n\x11u\xdaz\xd1 \x00\x00\u07d4\xa1pp\xc2\xe9\u0169@\xa4\xec\x0eIT\xc4\xd7\xd6C\xbe\x8fI\x89lk\x17\x03;6\x1c\x80\x00\u07d4\xa1|\x9eC#\x06\x95\x18\x18\x9dR\a\xa0r\x8d\u02d20j?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\x83`\xe9\x85\xf2\x06.\x8f\x8e\xfe\x02\xad,\xbc\x91\xad\x9aZ\xad\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa1\x91\x14\x05\xcfn\x99\x9e\xd0\x11\xf0\xdd\xcd*O\xf7\u008f%&\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa1\x92i\x80\a\xcc\x11\xaa`=\"\x1d_\xee\xa0v\xbc\xf7\xc3\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\x92\xf0j\xb0R\xd5\xfd\u007f\x94\xee\xa81\x8e\x82x\x15\xfegz\x89\a\x1f\x8a\x93\xd0\x1eT\x00\x00\u07d4\xa1\x99\x81D\x96\x8a\\p\xa6AUT\xce\xfe\u0082F\x90\u0125\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa1\xa1\xf0\xfam \xb5\nyO\x02\xefR\b\\\x9d\x03j\xa6\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xa1\xae\x8dE@\xd4\xdbo\xdd\xe7\x14oA[C\x1e\xb5\\y\x83\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xa1\xb4|M\x0e\xd6\x01\x88B\xe6\xcf\xc8c\n\u00e3\x14.^k\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xa1\xc4\xf4Z\x82\xe1\xc4x\xd8E\b.\xb1\x88u\xc4\xeae9\xab\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa1\xdc\xd0\xe5\xb0Z\x97|\x96#\xe5\xae/Y\xb9\xad\xa2\xf3>1\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa1\xe48\n;\x1ft\x96s\xe2p\"\x99\x93\xeeU\xf3Vc\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf1\x93\xa0Y/\x1f\xeb\x9f\xdf\xc9\n\xa8\x13xN\xb8\x04q\u0249K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa1\xf2\x85@P\xf8re\x8e\xd8.R\xb0\xad{\xbc\x1c\xb9!\xf6\x89m\x03\x17\xe2\xb3&\xf7\x00\x00\u07d4\xa1\xf5\xb8@\x14\rZ\x9a\xce\xf4\x02\xac<\u00c8jh\xca\xd2H\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa1\xf7e\xc4O\xe4_y\x06w\x94HD\xbeO-B\x16_\xbd\x89\xc7\xe9\xcf\xdev\x8e\xc7\x00\x00\u07d4\xa1\xf7\xdd\xe1\xd78\xd8\xcdg\x9e\xa1\xee\x96[\xee\"K\xe7\xd0M\x89=\x18DP\xe5\xe9<\x00\x00\u07d4\xa1\xf8\u063c\xf9\x0ew\u007f\x19\xb3\xa6Iu\x9a\xd9P'\xab\xdf\u00c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x02TrB\x80onp\xe7@X\xd6\xe5)-\xef\xc8\xc8\u0509l\x87T\xc8\xf3\f\b\x00\x00\u07d4\xa2\r\a\x1b\x1b\x000cI}y\x90\xe1$\x9d\xab\xf3l5\xf7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\r\x8f\xf6\f\xaa\xe3\x1d\x02\xe0\xb6e\xfaC]v\xf7|\x94B\x89\x1a\x8a\x90\x9d\xfc\xef@\x00\x00\u07d4\xa2\x11\xda\x03\xcc\x0e1\xec\xceS\t\x99\x87\x18QU(\xa0\x90\u07c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\x14B\xab\x054\n\xdeh\xc9\x15\xf3\xc39\x9b\x99U\xf3\xf7\xeb\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xa2\"\"Y\u075c>=\xed\x12p\x84\xf8\b\xe9*\x18\x870,\x89\b\xc83\x9d\xaf\xedH\x00\x00\u07d4\xa2*\xde\r\xdb\\n\xf8\xd0\u034d\xe9M\x82\xb1\x10\x82\xcb.\x91\x897KW\xf3\xce\xf2p\x00\x00\u07d4\xa2L:\xb6!\x81\xe9\xa1[x\xc4b\x1eL|X\x81'\xbe&\x89\b\xcd\xe4:\x83\xd31\x00\x00\u07d4\xa2W\xadYK\u0603(\xa7\xd9\x0f\xc0\xa9\a\u07d5\xee\xca\xe3\x16\x89\x1c7\x86\xff8F\x93\x00\x00\u07d4\xa2[\bd7\xfd!\x92\u0420\xf6On\xd0D\xf3\x8e\xf3\xda2\x89\x12)\x0f\x15\x18\v\xdc\x00\x00\u07d4\xa2v\xb0X\u02d8\u060b\xee\xdbg\xe5CPl\x9a\r\x94p\u0609\x90\xaa\xfcv\xe0/\xbe\x00\x00\u07d4\xa2\x82\xe9i\xca\xc9\xf7\xa0\xe1\xc0\u0350\xf5\xd0\xc48\xacW\r\xa3\x89\"\a\xeb\x89\xfc'8\x00\x00\xe0\x94\xa2\x91\xe9\u01d9\rU-\u046e\x16\u03bc?\xca4,\xba\xf1\u044a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa2\x93\x19\xe8\x10i\xe5\xd6\r\xf0\x0f=\xe5\xad\xee5\x05\xec\xd5\xfb\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa2\x96\x8f\xc1\xc6K\xac\vz\xe0\u058b\xa9I\x87Mm\xb2S\xf4\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa2\x9d[\xdat\xe0\x03GHr\xbdX\x94\xb8\x853\xffd\u00b5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa2\x9df\x1acv\xf6m\vt\xe2\xfe\x9d\x8f&\xc0$~\xc8L\x89\xdf3\x04\a\x9c\x13\xd2\x00\x00\u07d4\xa2\xa45\xdeD\xa0\x1b\xd0\ucc9eD\xe4vD\xe4j\f\xdf\xfb\x89\x1b\x1dDZz\xff\xe7\x80\x00\u07d4\xa2\xac\xe4\u0253\xbb\x1eS\x83\xf8\xact\xe1y\x06n\x81O\x05\x91\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa2\xb7\x01\xf9\xf5\xcd\u041eK\xa6+\xae\xba\u3a02W\x10X\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa2\u0145O\xf1Y\x9f\x98\x89,W%\xd2b\xbe\x1d\xa9\x8a\xad\xac\x89\x11\t\xff30\x10\xe7\x80\x00\u07d4\xa2\xc7\xea\xff\xdc,\x9d\x93sE l\x90\x9aR\u07f1LG\x8f\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa2\u04aabk\t\xd6\xd4\xe4\xb1?\u007f\xfcZ\x88\xbdz\xd3gB\x89\xfb\x80xPuS\x83\x00\x00\u07d4\xa2\u04cd\xe1\xc79\x06\xf6\xa7\xcan\xfe\xb9|\xf6\xf6\x9c\xc4!\xbe\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa2\xdce\xee%kY\xa5\xbdy)wO\x90K5\x8d\U000ed84a\x04\x83\xbc\xe2\x8b\xeb\t\xf8\x00\x00\u07d4\xa2\xe0h:\x80]\xe6\xa0^\xdb/\xfb\xb5\xe9o\x05p\xb67\u00c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa2\u1e2a\x90\x0e\x9c\x13\x9b?\xa1\"5OaV\xd9*\x18\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa2\u2d54\x1e\f\x01\x94K\xfe\x1d_\xb4\xe8\xa3K\x92,\u03f1\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xe4`\xa9\x89\xcb\x15V_\x9e\u0327\xd1!\xa1\x8eN\xb4\x05\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa2\xec\xce,I\xf7*\t\x95\xa0\xbd\xa5z\xac\xf1\xe9\xf0\x01\xe2*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa2\xf4r\xfeO\"\xb7}\xb4\x89!\x9e\xa4\x02=\x11X*\x93)\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa2\xf7\x98\xe0w\xb0}\x86\x12N\x14\a\xdf2\x89\r\xbbKcy\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa2\xf8k\xc0a\x88N\x9e\xef\x05d\x0e\xddQ\xa2\xf7\xc0Yli\x89llD\xfeG\xec\x05\x00\x00\u07d4\xa2\xfa\x17\xc0\xfbPl\xe4\x94\x00\x8b\x95W\x84\x1c?d\x1b\x8c\xae\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa3\x04X\x8f\r\x85\f\xd8\u04cfv\xe9\xe8<\x1b\xf6>3>\u0789\x02(V\x01!l\x8c\x00\x00\u07d4\xa3\x05\x8cQszN\x96\xc5_.\xf6\xbd{\xb3X\x16~\u00a7\x89 \xdb:\xe4H\x1a\u0500\x00\u07d4\xa3\t\xdfT\u02bc\xe7\f\x95\xec03\x14\x9c\xd6g\x8ao\xd4\u03c9\f\x1f\x12\xc7Q\x01X\x00\x00\u07d4\xa3\nER\x0eR\x06\xd9\x00@p\xe6\xaf>{\xb2\xe8\xddS\x13\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3\x0e\n\xcbSL\x9b0\x84\xe8P\x1d\xa0\x90\xb4\xeb\x16\xa2\xc0\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3 0\x95\xed\xb7\x02\x8ehq\xce\n\x84\xf5HE\x9f\x830\n\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa3!\t\x1d0\x18\x06By\xdb9\x9d+*\x88\xa6\xf4@\xae$\x89\xadx\xeb\u016cb\x00\x00\x00\u07d4\xa3#-\x06\x8dP\x06I\x03\xc9\xeb\xc5c\xb5\x15\xac\u0237\xb0\x97\x89l\x87T\xc8\xf3\f\b\x00\x00\xe0\x94\xa3$\x1d\x89\n\x92\xba\xf5)\b\xdcJ\xa0Irk\xe4&\xeb\u04ca\x04<-\xa6a\xca/T\x00\x00\u07d4\xa3)F&\xec)\x84\xc4;C\xdaM]\x8eFi\xb1\x1dKY\x896\xa4\xcfcc\x19\xc0\x00\x00\u07d4\xa3,\xf7\xdd\xe2\f=\xd5g\x9f\xf5\xe3%\x84\\p\u0156&b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa39\xa3\xd8\xca(\x0e'\xd2A[&\xd1\xfcy2(\xb6`C\x896\U00086577\x8f\xf0\x00\x00\u07d4\xa3<\xb4P\xf9[\xb4n%\xaf\xb5\x0f\xe0_\xee\xe6\xfb\x8c\xc8\xea\x89*\x11)\u0413g \x00\x00\u07d4\xa3?p\xdaru\xef\x05q\x04\u07e7\xdbd\xf4r\xe9\xf5\xd5S\x89\x04YF\xb0\xf9\xe9\xd6\x00\x00\u07d4\xa3@v\xf8K\xd9\x17\xf2\x0f\x83B\u024b\xa7\x9eo\xb0\x8e\xcd1\x89\u3bb5sr@\xa0\x00\x00\u07d4\xa3C\x0e\x1fd\u007f2\x1e\xd3G9V##\xc7\xd6#A\vV\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xa3O\x9dV\x8b\xf7\xaf\xd9L*[\x8a_\xf5\\f\xc4\by\x99\x89\x84}P;\"\x0e\xb0\x00\x00\u07d4\xa3V\x06\xd5\x12 \xee\u007f!F\xd4\x11X.\xe4\xeeJEYn\x89\u062a\xbe\b\v\xc9@\x00\x00\u07d4\xa3VU\x1b\xb7}OE\xa6\xd7\xe0\x9f\n\b\x9ey\u0322I\u02c9\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xa3\\\x19\x13,\xac\x195Wj\xbf\xedl\x04\x95\xfb\a\x88\x1b\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3e\x91\x8b\xfe?&'\xb9\xf3\xa8gu\xd8un\x0f\u0629K\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa3n\r\x94\xb9Sd\xa8&q\xb6\b\xcb-72Ea)\t\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xa3u\xb4\xbc$\xa2N\x1fyu\x93\xcc0+/3\x10c\xfa\\\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3v\"\xac\x9b\xbd\xc4\xd8+u\x01]t[\x9f\x8d\xe6Z(\uc25d\xc0\\\xce(\u00b8\x00\x00\xe0\x94\xa3y\xa5\a\fP=/\xac\x89\xb8\xb3\xaf\xa0\x80\xfdE\xedK\xec\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xa3\x80-\x8ae\x9e\x89\xa2\xc4~\x90T0\xb2\xa8'\x97\x89P\xa7\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa3\x83\x06\xcbp\xba\xa8\u4446\xbdh\xaap\xa8=$/)\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x84vi\x1d4\x94.\xeak/v\x88\x92#\x04}\xb4az\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\x87\xceN\x96\x1axG\xf5`\a\\d\xe1YkVA\xd2\x1c\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xa3\x87\xec\xde\x0e\xe4\xc8\a\x94\x99\xfd\x8e\x03G;\u060a\xd7R*\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\x88:$\xf7\xf1f _\x1aj\x99I\al&\xa7nqx\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xa3\x8b[\xd8\x1a\x9d\xb9\u04b2\x1d^\xc7\xc6\x05R\xcd\x02\xedV\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa3\x90\xca\x12+\x85\x01\xee>^\a\xa8\xcaKA\x9f~M\xae\x15\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x93*1\xd6\xffu\xfb;\x12q\xac\xe7\u02a7\xd5\xe1\xff\x10Q\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xa3\x94\xadO\xd9\xe6S\x0eo\\S\xfa\xec\xbe\u0781\xcb\x17-\xa1\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xa3\x97\x9a\x92v\n\x13Z\xdfi\xd7/u\xe1gu_\x1c\xb8\u00c9\x05k\xc7^-c\x10\x00\x00\xe0\x94\xa3\x9b\xfe\xe4\xae\u027du\xbd\"\u01b6r\x89\x8c\xa9\xa1\xe9]2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa3\xa2b\xaf\u0493h\x19#\b\x92\xfd\xe8O-ZYJ\xb2\x83\x89e\xea=\xb7UF`\x00\x00\u07d4\xa3\xa2\xe3\x19\xe7\u04e1D\x8bZ\xa2F\x89S\x16\f-\xbc\xbaq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\xa5{\a\x16\x13(\x04\xd6\n\xac(\x11\x97\xff+=#{\x01\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xa3\xa9>\xf9\xdb\xea&6&=\x06\xd8I/jA\u0790|\"\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xa3\xae\x18y\x00}\x80\x1c\xb5\xf3RqjM\u063a'!\xde=\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xa3\xba\r:6\x17\xb1\xe3\x1bNB,\xe2i\xe8s\x82\x8d]i\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xa3\xbc\x97\x9bp\x80\t/\xa1\xf9/n\x0f\xb3G\u2359PE\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa3\xbf\xf1\u07e9\x97\x16h6\f\r\x82\x82\x842\xe2{\xf5Ng\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa3\xc1J\xce(\xb1\x92\u02f0b\x14_\u02fdXi\xc6rq\xf6\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa3\xc3:\xfc\x8c\xb4pN#\x15=\xe2\x04\x9d5\xaeq3$r\x89+X\xad\u06c9\xa2X\x00\x00\u07d4\xa3\u0430<\xff\xbb&\x9fyj\u009d\x80\xbf\xb0}\xc7\u01ad\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa3\u0543\xa7\xb6[#\xf6\vy\x05\xf3\xe4\xaab\xaa\xc8\u007fB'\x898\xbe\xfa\x12mZ\x9f\x80\x00\u07d4\xa3\xdb6J3-\x88K\xa9;&\x17\xaeM\x85\xa1H\x9b\xeaG\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xa3\xe0Q\xfbtJ\xa3A\f;\x88\xf8\x99\xf5\xd5\u007f\x16\x8d\xf1-\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xa3\xe3\xa6\xeaP\x95s\xe2\x1b\xd0#\x9e\xce\x05#\xa7\xb7\u061b/\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xa3\xf4\xad\x14\xe0\xbbD\xe2\xce,\x145\x9cu\xb8\xe72\xd3pT\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa3\xfa\xccP\x19\\\vI3\xc8X\x97\xfe\xcc[\xbd\x99\\4\xb8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\x03Z\xb1\xe5\x18\b!\xf0\xf3\x80\xf1\x13\x1bs\x87\xc8\u0641\u0349\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\n\xa2\xbb\xce\fr\xb4\xd0\xdf\xff\xccBq[+T\xb0\x1b\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x19\xa9\x84\x14#c&uuV`\x894\x0e\xea\x0e\xa2\b\x19\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xa4!\u06f8\x9b:\aA\x90\x84\xad\x10\xc3\xc1]\xfe\x9b2\xd0\u008a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\"\xe4\xbf\v\xf7AG\u0309[\xed\x8f\x16\xd3\xce\xf3BaT\x89\x12\xef?b\xee\x116\x80\x00\u07d4\xa4%\x9f\x83E\xf7\u3a37+\x0f\xec,\xf7^2\x1f\xdaM\u0089g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xa4)\b\xe7\xfeS\x98\n\x9a\xbf@D\xe9W\xa5Kp\u973e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4)\xfa\x88s\x1f\xdd5\x0e\x8e\xcdn\xa5B\x96\xb6HO\u6549j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\xa40\x99]\xdb\x18[\x98e\xdb\xe6%9\xad\x90\xd2.Ks\u008a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa46\xc7TS\xcc\xcaJ\x1f\x1bb\xe5\u0123\r\x86\xdd\xe4\xbeh\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa47\xfen\xc1\x03\u028d\x15\x8fc\xb34\"N\u032c[>\xa3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4;m\xa6\xcbz\xacW\x1d\xff'\xf0\x9d9\xf8F\xf57i\xb1\x89\x14\x99\x8f2\xacxp\x00\x00\u07d4\xa4;\x81\xf9\x93V\xc0\xaf\x14\x1a\x03\x01\rw\xbd\x04,q\xc1\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4>\x19G\xa9$+5Ua\xc3\n\x82\x9d\xfe\uc881Z\xf8\x89\xd2=\x99\x96\x9f\u0591\x80\x00\u07d4\xa4H\x9aP\xea\xd5\xd5DZ{\xeeM-U6\u00a7lA\xf8\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4O\xe8\x00\xd9o\xca\xd7;qp\xd0\xf6\x10\u02cc\x06\x82\xd6\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa4T2\xa6\xf2\xac\x9dVW{\x93\x8a7\xfa\xba\xc8\xcc|F\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4f\xd7p\u0618\xd8\xc9\xd4\x05\xe4\xa0\xe5Q\xef\xaf\xcd\xe5<\xf9\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\xe0\x94\xa4g\a1\x17X\x93\xbb\xcf\xf4\xfa\x85\u0397\xd9O\xc5\x1cK\xa8\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4kC\x87\xfbM\xcc\xe0\x11\xe7nMsT}D\x81\xe0\x9b\xe5\x89Hz\x9a0E9D\x00\x00\u07d4\xa4l\xd27\xb6>\xeaC\x8c\x8e;e\x85\xf6y\xe4\x86\b2\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4wy\u063c\x1c{\xce\x0f\x01\x1c\xcb9\xefh\xb8T\xf8\u078f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa4\x82kl8\x82\xfa\xd0\xed\\\x8f\xbb%\xcc@\xccO3u\x9f\x89p\x1bC\xe3D3\xd0\x00\x00\u07d4\xa4\x87Y(E\x8e\xc2\x00]\xbbW\x8c\\\xd35\x80\xf0\xcf\x14R\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa4\x9fR:\xa5\x13d\xcb\xc7\u0655\x16=4\xebY\r\xed/\b\x89\x90'B\x1b*\x9f\xbc\x00\x00\u07d4\xa4\xa4\x9f\v\xc8h\x8c\xc9\xe6\xdc\x04\xe1\xe0\x8dR\x10&\xe6Ut\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa4\xa7\xd3\x06\xf5\x10\xcdX5\x94(\xc0\xd2\xf7\xc3`\x9dVt\u05c9\xb5\x8c\xb6\x1c<\xcf4\x00\x00\u07d4\xa4\xa8:\a8y\x9b\x97\x1b\xf2\xdep\x8c.\xbf\x91\x1c\xa7\x9e\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa4\xb0\x9d\xe6\xe7\x13\xdciTnv\xef\n\xcf@\xb9O\x02A\xe6\x89\x11}\xc0b~\xc8p\x00\x00\xe0\x94\xa4\u04b4)\xf1\xadSI\xe3\x17\x04\x96\x9e\xdc_%\xee\x8a\xca\x10\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xa4\xd6\xc8.\u076eYG\xfb\xe9\xcd\xfb\xd5H\xae3\xd9\x1aq\x91\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa4\xda4E\r\"\xec\x0f\xfc\xed\xe0\x00K\x02\xf7\x87.\xe0\xb7:\x89\x05\x0fafs\xf0\x83\x00\x00\xe0\x94\xa4\xddY\xab^Q}9\x8eI\xfaS\u007f\x89\x9f\xedL\x15\xe9]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa4\xe6#E\x1e~\x94\xe7\u86e5\xed\x95\u0228:b\xff\xc4\xea\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa4\xed\x11\xb0r\u061f\xb16u\x9f\u019bB\x8cH\xaa]L\xed\x89\x0e?\x15'\xa0<\xa8\x00\x00\u07d4\xa4\xfb\x14@\x9ag\xb4V\x88\xa8Y>\\\xc2\xcfYl\xedo\x11\x89a\t=|,m8\x00\x00\xe0\x94\xa5\x14\xd0\x0e\xddq\b\xa6\xbe\x83\x9ac\x8d\xb2AT\x18\x17A\x96\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\xe0\x94\xa5\"\xde~\xb6\xae\x12PR*Q13\xa9;\xd4(IG\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa5$\xa8\xcc\xccIQ\x8d\x17\n2\x82p\xa2\xf8\x813\xfb\xaf]\x89\x0f\xf7\x02-\xac\x10\x8a\x00\x00\u07d4\xa59\xb4\xa4\x01\xb5\x84\xdf\xe0\xf3D\xb1\xb4\"\xc6UC\x16~.\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa5>\xadT\xf7\x85\n\xf2\x148\xcb\xe0z\xf6\x86'\x9a1[\x86\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5C\xa0f\xfb2\xa8f\x8a\xa0sj\f\x9c\xd4\rx\t\x87'\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa5gw\vj\xe3 \xbd\xdeP\xf9\x04\xd6c\xe7F\xa6\x1d\xac\xe6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5h\xdbMW\xe4\xd6tb\xd73\u019a\x9e\x0f\xe2n!\x83'\x89;k\xff\x92f\xc0\xae\x00\x00\u07d4\xa5i\x8059\x1eg\xa4\x90\x13\xc0\x00 yY1\x14\xfe\xb3S\x89\r\x02\xabHl\xed\xc0\x00\x00\u07d4\xa5p\":\xe3\u02a8QA\x8a\x98C\xa1\xacU\xdbH$\xf4\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa5s`\xf0\x02\xe0\xd6M-tE}\x8c\xa4\x85~\xe0\v\xcd\u07c9\x123\xe22\xf6\x18\xaa\x00\x00\u07d4\xa5u\xf2\x89\x1d\xcf\u0368<\\\xf0\x14t\xaf\x11\xee\x01\xb7-\u0089\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\xa5x;\xf342\xff\x82\xacI\x89\x85\xd7\xd4`\xaeg\xec6s\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xa5\x87MuF5\xa7b\xb3\x81\xa5\xc4\u01d2H:\xf8\xf2=\x1d\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa5\xa4\"\u007fl\xf9\x88%\xc0\u057a\xffS\x15u,\xcc\x1a\x13\x91\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa5\xabK\xd3X\x8fF\xcb'.V\xe9=\xee\u04c6\xba\x8bu=\x89HB\xf0A\x05\x87,\x80\x00\xe0\x94\xa5\xba\xd8e\t\xfb\xe0\xe0\xe3\xc0\xe9?m8\x1f\x1a\xf6\xe9\u0501\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa5\xc36\b;\x04\xf9G\x1b\x8cn\xd76y\xb7Mf\xc3c\uc263e\nL\x9d \xe2\x00\x00\u07d4\xa5\xcd\x129\x92\x19K4\xc4x\x13\x140;\x03\xc5IH\xf4\xb9\x89l\xfc\xc3\xd9\x1d\xa5c\x00\x00\u07d4\xa5\u0578\xb6-\x00-\xef\x92A7\x10\xd1;o\xf8\xd4\xfc}\u04c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xa5\xd9ni}F5\x8d\x11\x9a\xf7\x81\x9d\xc7\b\u007fj\xe4\u007f\xef\x8a\x03\x17\xbe\xe8\xaf3\x15\xa7\x80\x00\u07d4\xa5\xde^CO\xdc\xddh\x8f\x1c1\xb6\xfbQ,\xb1\x96rG\x01\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xa5\xe0\xfc<:\xff\xed=\xb6q\tG\xd1\xd6\xfb\x01\u007f>'m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9;I\xea|P\x9d\xe7\xc4Ml\xfe\xdd\xefY\x10\u07aa\xf2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa5\xe9\xcdKt%]\"\xb7\u0672z\xe8\xddC\xedn\xd0%+\x89)\x8d\xb2\xf5D\x11\u0640\x00\xe0\x94\xa5\xf0\a{5\x1flP\\\xd5\x15\u07e6\xd2\xfa\u007f\\L\u0487\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xa5\xf0u\xfd@\x135W{f\x83\u0081\xe6\xd1\x01C-\xc6\xe0\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xa5\xfe,\xe9\u007f\x0e\x8c8V\xbe\r\xe5\xf4\u0732\xce]8\x9a\x16\x89\x01=\xb0\xb8\xb6\x86>\x00\x00\u07d4\xa5\xffb\"-\x80\xc0\x13\xce\xc1\xa0\xe8\x85\x0e\xd4\xd3T\xda\xc1m\x89\vA\a\\\x16\x8b\x18\x00\x00\u07d4\xa6\t\xc2m\xd3P\xc25\xe4K+\x9c\x1d\xdd\xcc\u0429\xd9\xf87\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\f\x12\tuO]\x87\xb1\x81\xdaO\b\x17\xa8\x18Y\xef\x9f\u0609\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\xe0\x94\xa6\x10\x1c\x96\x1e\x8e\x1c\x15y\x8f\xfc\xd0\xe3 \x1dw\x86\xec7:\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa6\x13Ei\x96@\x8a\xf1\xc2\xe9>\x17w\x88\xabU\x89^+2\x8a\x01Y\x19\xffG|\x88\xb8\x00\x00\u07d4\xa6\x18\x87\x81\x8f\x91J \xe3\x10w)\v\x83qZk-n\xf9\x89e\xea=\xb7UF`\x00\x00\u07d4\xa6\x1aT\xdfxJD\xd7\x1bw\x1b\x871u\t!\x13\x81\xf2\x00\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa6\x1c\u06ed\xf0K\x1eT\u0203\xde`\x05\xfc\xdf\x16\xbe\xb8\xeb/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa69\xac\xd9k1\xbaS\xb0\u0407c\"\x9e\x1f\x06\xfd\x10^\x9d\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6BP\x10\x04\xc9\x0e\xa9\xc9\xed\x19\x98\xba\x14\nL\xd6,o_\x89\r\x94\xfb\x8b\x10\xf8\xb1\x80\x00\u07d4\xa6D\xed\x92,\xc27\xa3\xe5\u0117\x9a\x99Tw\xf3nP\xbcb\x89\x1f\xa7=\x84]~\x96\x00\x00\u07d4\xa6F\xa9\\moY\xf1\x04\xc6T\x1dw`uz\xb3\x92\xb0\x8c\x89\u3bb5sr@\xa0\x00\x00\xe0\x94\xa6HL\u0184\xc4\xc9\x1d\xb5>\xb6\x8aM\xa4Zjk\xda0g\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa6N_\xfbpL,\x919\xd7~\xf6\x1d\x8c\u07e3\x1dz\x88\xe9\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xa6T&\xcf\xf3x\xed#%5\x13\xb1\x9fIm\xe4_\xa7\u13ca\x01\x86P\x12|\xc3\u0700\x00\x00\u07d4\xa6jIc\xb2\u007f\x1e\xe1\x93+\x17+\xe5\x96N\r:\xe5KQ\x89\t`\xdbwh\x1e\x94\x00\x00\u07d4\xa6\u007f8\x81\x95eB:\xa8_>:\xb6\x1b\xc7c\u02eb\x89\u0749sw\xb0\"\u01be\b\x00\x00\u07d4\xa6\x8c14E\xc2-\x91\x9e\xe4l\xc2\xd0\xcd\xff\x04:uX%\x89\x04\x13t\xfd!\xb0\u0600\x00\u07d4\xa6\x8e\f0\u02e3\xbcZ\x88>T\x03 \xf9\x99\xc7\xcdU\x8e\\\x89a\x9237b\xa5\x8c\x80\x00\u07d4\xa6\x90\xf1\xa4\xb2\n\xb7\xba4b\x86 \u079c\xa0@\xc4<\x19c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa6\x9d|\xd1}HB\xfe\x03\xf6*\x90\xb2\xfb\xf8\xf6\xaf{\xb3\x80\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa6\xa0\x82R\xc8YQw\xcc.`\xfc'Y>#y\xc8\x1f\xb1\x89\x01\x16Q\xac>zu\x80\x00\u07d4\xa6\xa0\xdeB\x1a\xe5Om\x17(\x13\b\xf5dm/9\xf7w]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa6\xb2\xd5s)s`\x10,\a\xa1\x8f\xc2\x1d\xf2\xe7I\x9f\xf4\xeb\x89\xd9o\u0390\u03eb\xcc\x00\x00\xe0\x94\xa6\xc9\x10\xceMIJ\x91\x9c\u036a\xa1\xfc;\x82\xaat\xba\x06\u03ca\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xa6\u3ea3\x8e\x10J\x1e'\xa4\xd8(i\xaf\xb1\xc0\xaen\xff\x8d\x89\x01\x11@\ueb4bq\x00\x00\u07d4\xa6\xee\xbb\xe4d\u04d1\x87\xbf\x80\u029c\x13\xd7 '\xec[\xa8\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa6\xf6+\x8a=\u007f\x11\"\a\x01\xab\x9f\xff\xfc\xb3'\x95\x9a'\x85\x89\x1bn)\x1f\x18\u06e8\x00\x00\u07d4\xa6\xf93\a\xf8\xbc\xe01\x95\xfe\u0387 C\xe8\xa0?{\xd1\x1a\x89\x9csK\xadQ\x11X\x00\x00\u07d4\xa7\x01\xdfy\xf5\x94\x90\x1a\xfe\x14DH^k \u00fd\xa2\xb9\xb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa7\x02L\xfdt,\x1e\xc1<\x01\xfe\xa1\x8d0B\xe6_\x1d]\xee\x8a\x02c\x11\x9a(\xab\u0430\x80\x00\u07d4\xa7\x18\xaa\xadY\xbf9\\\xba+#\xe0\x9b\x02\xfe\f\x89\x81bG\x8960<\x97\xe4hx\x00\x00\u07d4\xa7$|S\xd0Y\xeb|\x93\x10\xf6(\xd7\xfclj\nw?\b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xa7%7c\xcfJu\u07d2\xca\x1evm\xc4\xee\x8a'E\x14{\x8a\x02F7p\xe9\n\x8fP\x00\x00\u07d4\xa7.\xe6f\u0133^\x82\xa5\x06\x80\x8bD<\xeb\xd5\xc62\xc7\u0749+^:\xf1k\x18\x80\x00\x00\u07d4\xa7DD\xf9\x0f\xbbT\xe5o:\u0276\xcf\u032aH\x19\xe4aJ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa7GC\x9a\xd0\u04d3\xb5\xa08a\xd7r\x962m\u8edd\xb9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xa7`{BW;\xb6\xf6\xb4\xd4\xf2<~*&\xb3\xa0\xf6\xb6\xf0\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xa7i)\x89\n{G\xfb\x85\x91\x96\x01lo\u0742\x89\u03b7U\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u0794\xa7kt?\x98\x1bi0r\xa11\xb2+\xa5\x10\x96\\/\xef\u05c8\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xa7m?\x15bQ\xb7,\f\xcfKG\xa39<\xbdoI\xa9\u0149Hz\x9a0E9D\x00\x00\u07d4\xa7t(\xbc\xb2\xa0\xdbv\xfc\x8e\xf1\xe2\x0eF\x1a\n2\u016c\x15\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xa7u\x8c\xec\xb6\x0e\x8faL\u0396\x13~\xf7+O\xbd\awJ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7w^J\xf6\xa2:\xfa \x1f\xb7\x8b\x91^Q\xa5\x15\xb7\xa7(\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xa7\u007f>\u1793\x88\xbb\xbb\"\x15\xc6#\x97\xb9e`\x13#`\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa7\x85\x9f\xc0\u007fun\xa7\xdc\xeb\xbc\xcdB\xf0X\x17X-\x97?\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa7\x96lH\x9fLt\x8az\u902a'\xa5t%\x17g\xca\xf9\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xa7\xa3\xbba9\xb0\xad\xa0\f\x1f\u007f\x1f\x9fV\u0654\xbaM\x1f\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa7\xa3\xf1S\xcd\u00c8!\xc2\f]\x8c\x82A\xb2\x94\xa3\xf8+$\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa7\xa5\x17\u05ed5\x82\v\t\u0517\xfa~U@\xcd\xe9IXS\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xa7\xc9\u04c8\xeb\xd8s\xe6k\x17\x13D\x83\x97\xd0\xf3\u007f\x8b\u04e8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa7\u073b\xa9\xb9\xbfgb\xc1EAlPjq\u3d17 \x9c\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xa7\xe7O\v\xdb'\x8f\xf0\xa8\x05\xa6Ha\x8e\xc5+\x16o\xf1\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa7\xe87r\xbc \x0f\x90\x06\xaa*&\r\xba\xa8H=\xc5+0\x89\vB\xd56f7\xe5\x00\x00\xe0\x94\xa7\xef5\u0387\xed\xa6\u008d\xf2HxX\x15\x05>\xc9zPE\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xa7\xf9\"\f\x80G\x82k\xd5\xd5\x18?Ngjmw\xbf\xed6\x89\bPh\x97k\xe8\x1c\x00\x00\u07d4\xa8\a\x10O'\x03\xd6y\xf8\u07af\xc4B\xbe\xfe\x84\x9eB\x95\v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\f\xb1s\x8b\xac\b\xd4\xf9\xc0\x8bM\xef\xf5\x15T_\xa8XO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa8\x19\xd2\xec\xe1\"\xe0(\xc8\xe8\xa0J\x06M\x02\xb9\x02\x9b\b\xb9\x8965\u026d\xc5\u07a0\x00\x00\u0794\xa8%\xfdZ\xbby&\xa6|\xf3k\xa2F\xa2K\xd2{\xe6\xf6\xed\x88\xf4?\xc2\xc0N\xe0\x00\x00\u07d4\xa8(U9\x86\x9d\x88\xf8\xa9aS7Uq}~\xb6Uv\xae\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xa83\x82\xb6\xe1Rg\x97J\x85P\xb9\x8fqv\xc1\xa3S\xf9\xbe\x89\xbf\xfd\xaf/\xc1\xb1\xa4\x00\x00\xe0\x94\xa8DlG\x81\xa77\xacC(\xb1\xe1[\x8a\v?\xbb\x0f\xd6h\x8a\x04\x87\x94\xd1\xf2F\x19*\x00\x00\u07d4\xa8E[A\x17e\u0590\x1e1\x1erd\x03\t\x1eB\xc5f\x83\x89\xb7:\xec;\xfe\x14P\x00\x00\xe0\x94\xa8f\x13\xe6\u0124\xc9\xc5_\\\x10\xbc\xda2\x17]\u02f4\xaf`\x8a\x02C\xd6\xc2\xe3k\xe6\xae\x00\x00\u07d4\xa8m\xb0}\x9f\x81/G\x96b-@\xe0=\x13Xt\xa8\x8at\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa8\u007fz\xbdo\xa3\x11\x94(\x96x\xef\xb6<\xf5\x84\xee^*a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xa8\x80\u2a3f\x88\xa1\xa8&H\xb4\x01<I\xc4YLC<\u020a\x01\x00N.E\xfb~\xe0\x00\x00\u07d4\xa8\x85w\xa0s\xfb\xaf3\xc4\xcd .\x00\xeap\xefq\x1b@\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\x91L\x95\xb5`\xec\x13\xf1@Ws8\xc3+\u02f7}:z\x89\t\xc2\x00vQ\xb2P\x00\x00\xe0\x94\xa8\x9a\xc9;#7\x04r\u06ac3~\x9a\xfd\xf6BT?>W\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\x9d\xf3HY\xed\xd7\xc8 \u06c8w@\xd8\xff\x9e\x15\x15|{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xa4<\x00\x91\x00al\xb4\xaeN\x03?\x1f\xc5\xd7\xe0\xb6\xf1R\x89\u0548\xd0x\xb4?M\x80\x00\u07d4\xa8\xa7\b\xe8O\x82\u06c6\xa3U\x02\x19;Ln\xe9\xa7n\xbe\x8f\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xa8\xa7\xb6\x8a\u06b4\xe3\xea\xdf\xf1\x9f\xfaX\xe3J?\xce\xc0\xd9j\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xa8\xa8\xdb\xdd\x1a\x85\u047e\xee%i\xe9\x1c\xccM\t\xae\u007fn\xa1\x8a\x01:k+VHq\xa0\x00\x00\u07d4\xa8\xac\xa7H\xf9\xd3\x12\xect\u007f\x8bex\x14&\x94\xc7\xe9\xf3\x99\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa8\xb6[\xa3\x17\x1a?w\xa65\v\x9d\xaf\x1f\x8dU\xb4\xd2\x01\xeb\x89(b\xf3\xb0\xd2\"\x04\x00\x00\u07d4\xa8\xbe\xb9\x1c+\x99\u0216J\xa9[kJ\x18K\x12i\xfc4\x83\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u0794\xa8\xc0\xb0/\xaf\x02\xcbU\x19\u0768\x84\xde{\xbc\x8c\x88\xa2\u0681\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xa8\xc1\u05aaA\xfe=e\xf6{\xd0\x1d\xe2\xa8f\xed\x1e\u066eR\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xa8\xca\xfa\xc3\"\x80\xd0!\x02\v\xf6\xf2\xa9x(\x83\u05ea\xbe\x12\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xa8\xdb\v\x9b \x14S3<u\u007fj\u067c\xb5U\xc0-\xa9;\x89wB\xb7\x83\x0f4\x1d\x00\x00\u07d4\xa8\xe4*N3\xd7Rl\xca\x19\u0663m\xcdn\x80@\xd0\xeas\x89:\x8c\x02\xc5\xea-\xe0\x00\x00\u07d4\xa8\xe7 \x1f\xf6\x19\xfa\xff\xc32\xe6\xad7\xedA\xe3\x01\xbf\x01J\x89 \x86\xac5\x10R`\x00\x00\u07d4\xa8\xee\x1d\xf5\xd4K\x12\x84i\xe9\x13V\x9e\xf6\xac\x81\xee\xdaO\u0209\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xa8\xef\x9a\xd2tC`B\x90>A<;\fb\xf5\xf5.\u0544\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa8\xf3\u007f\n\xb3\xa1\xd4H\xa9\xe3\xce@\x96_\x97\xa6F\b:4\x89\x11\xe0\xe4\xf8\xa5\v\xd4\x00\x00\u07d4\xa8\xf8\x9d\xd5\xccnd\u05f1\xee\xac\xe0\a\x02\x02,\xd7\xd2\xf0=\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xa9\x04v\xe2\xef\xdf\xeeO8{\x0f2\xa5\x06x\xb0\xef\xb5s\xb5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\x14PF\xfa6(\xcf_\xd4\xc6\x13\x92{\xe51\xe6\xdb\x1f\u0749\x06\x12O\xee\x99;\xc0\x00\x00\u07d4\xa9\x14\u0375q\xbf\xd9=d\xdaf\xa4\xe1\b\xea\x13NP\xd0\x00\x89M\x878\x99G\x13y\x80\x00\xe0\x94\xa9\x1aZ{4\x1f\x99\xc55\x14N \xbe\x9ck;\xb4\u008eM\x8a\x01&u:\xa2$\xa7\v\x00\x00\u07d4\xa9%%Q\xa6$\xaeQ7\x19\u06beR\a\xfb\xef\xb2\xfdwI\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xa9'\u050b\xb6\u02c1K\xc6\t\xcb\u02a9\x15\x1f]E\x9a'\xe1\x89\x0e\xb95\t\x00d\x18\x00\x00\u07d4\xa9)\u023dq\xdb\f0\x8d\xac\x06\b\n\x17G\xf2\x1b\x14e\xaa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xa9K\xbb\x82\x14\u03cd\xa0\xc2\xf6h\xa2\xacs\xe8bHR\x8dK\x894\n\xad!\xb3\xb7\x00\x00\x00\u07d4\xa9Q\xb2D\xffP\u03eeY\x1d^\x1a\x14\x8d\xf6\xa98\xef*\x1a\x89^\x00\x15\x84\xdf\xcfX\x00\x00\u07d4\xa9`\xb1\xca\xdd;\\\x1a\x8el\xb3\xab\xca\xf5.\xe7\xc3\xd9\xfa\x88\x89R\x8b\xc3T^Rh\x00\x00\u07d4\xa9a\x17\x1fSB\xb1s\xddp\xe7\xbf\xe5\xb5\xca#\x8b\x13\xbc\u0749\xb8'\x94\xa9$O\f\x80\x00\u07d4\xa9u\xb0w\xfc\xb4\u030e\xfc\xbf\x83\x84Y\xb6\xfa$:AY\u0589\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xa9{\xeb:H\xc4_\x15((L\xb6\xa9_}\xe4S5\x8e\u018a\x06\x90\x83l\n\xf5\xf5`\x00\x00\u07d4\xa9~\a!DI\x9f\xe5\xeb\xbd5J\xcc~~\xfbX\x98]\b\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xa9\x86v/zO)O.\v\x172y\xad,\x81\xa2\"4X\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xa9\x8f\x10\x985\xf5\xea\xcd\x05Cd|4\xa6\xb2i\xe3\x80/\xac\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xa9\x97\xdf\u01d8j'\x05\bH\xfa\x1cd\u05e7\xd6\xe0z\u0322\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xa9\x99\x91\u03bd\x98\xd9\xc88\xc2_zt\x16\xd9\xe2D\xca%\r\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xa9\xa1\xcd\xc3;\xfd7o\x1c\rv\xfbl\x84\xb6\xb4\xac'Mh\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xa9\xa8\xec\xa1\x1a#\xd6F\x89\xa2\xaa>A}\xbb=3k\xb5\x9a\x89\x0e4S\xcd;g\xba\x80\x00\u07d4\xa9\xac\xf6\x00\b\x1b\xb5[\xb6\xbf\xba\xb1\x81_\xfcN\x17\xe8Z\x95\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xa9\xad\x19&\xbcf\xbd\xb31X\x8e\xa8\x197\x88SM\x98,\x98\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xa9\xaf!\xac\xbeH/\x811\x89j\"\x806\xbaQ\xb1\x94S\u00c9\x02\xb5\xe0!\x98\f\xc1\x80\x00\xe0\x94\xa9\xb2\xd2\xe0IN\xab\x18\xe0}7\xbb\xb8V\xd8\x0e\x80\xf8L\u04ca\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xa9\xbaoA;\x82\xfc\xdd\xf3\xaf\xfb\xbd\u0412\x87\xdc\xf5\x04\x15\u0289\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xa9\xbe\x88\xad\x1eQ\x8b\v\xbb\x02J\xb1\xd8\xf0\xe7?y\x0e\fv\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xa9\xbf\xc4\x10\xdd\xdb q\x1eE\xc0s\x87\xea\xb3\n\x05N\x19\xac\x89>\x99`\x1e\xdfNS\x00\x00\u07d4\xa9\u0522\xbc\xbe[\x9e\bi\xd7\x0f\x0f\xe2\xe1\u05aa\xcdE\xed\u0149\n\xc6\xe7z\xb6c\xa8\x00\x00\xe0\x94\xa9\xd6KO;\xb7\x85\a\"\xb5\x8bG\x8b\xa6\x917^\"NB\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xa9\xd6\xf8q\xcax\x1au\x9a \xac:\u06d7,\xf1()\xa2\b\x892$\xf4'#\xd4T\x00\x00\xe0\x94\xa9\xdc\x04$\u0196\x9dy\x83X\xb3\x93\xb1\x93:\x1fQ\xbe\xe0\n\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xa9\xe1\x94f\x1a\xacpN\xe9\u07a0C\x97N\x96\x92\xde\xd8J]\x89\x1a&\xa5\x14\"\xa0p\x00\x00\u07d4\xa9\xe2\x837\xe65q\x93\xd9\xe2\xcb#k\x01\xbeD\xb8\x14'\u07c9wC\"\x17\xe6\x83`\x00\x00\u07d4\xa9\xe6\xe2^ekv%Xa\x9f\x14z!\x98[\x88t\xed\xfe\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xa9\xe9\xdb\xcez,\xb06\x94y\x98\x97\xbe\xd7\xc5M\x15_\u06a8\x89\n\xb5\xae\x8f\u025de\x80\x00\u07d4\xa9\xed7{}n\xc2Yq\xc1\xa5\x97\xa3\xb0\xf3\xbe\xadW\u024f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x02\x00\xf1\xd1~\x9cT\xda\x06G\xbb\x969]W\xa7\x858\u06099>\xf1\xa5\x12|\x80\x00\x00\u07d4\xaa\f\xa3ss7\x17\x8a\f\xaa\xc3\t\x9cXK\x05lV0\x1c\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xaa\x13kG\x96+\xb8\xb4\xfbT\r\xb4\xcc\xf5\xfd\xd0B\xff\xb8\u03c9\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xaa\x14B-o\n\xe5\xa7X\x19N\xd1W\x80\xc88\xd6\u007f\x1e\xe1\x8a\x06\t2\x05lD\x9d\xe8\x00\x00\u07d4\xaa\x16&\x9a\xac\x9c\r\x800h\xd8/\u01d1Q\xda\xdd3Kf\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xaa\x16p&\u04da\xb7\xa8V5\x94N\xd9\xed\xb2\xbf\xeb\xa1\x18P\x8a\x01\xc1\xd5\xe2\x1bO\xcfh\x00\x00\u07d4\xaa\x1b7h\xc1m\x82\x1fX\x0ev\xc8\xe4\xc8\xe8m}\u01c8S\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xaa\x1d\xf9.Q\xdf\xf7\v\x19s\xe0\xe9$\xc6b\x87\xb4\x94\xa1x\x89\x1c\xf8J0\xa0\xa0\xc0\x00\x00\u07d4\xaa,g\x00\x96\xd3\xf990S%B~\xb9U\xa8\xa6\r\xb3\u0149l\x95Y\x06\x99#-\x00\x00\u07d4\xaa15\xcbT\xf1\x02\xcb\xef\xe0\x9e\x96\x10:\x1ayg\x18\xffT\x89\x03\"\"\xd9\xc31\x94\x00\x00\u07d4\xaa2\x1f\xdb\xd4I\x18\r\xb8\xdd\xd3O\x0f\xe9\x06\xec\x18\xee\t\x14\x89%\"H\u07b6\xe6\x94\x00\x00\xe0\x94\xaa9%\xdc\"\v\xb4\xae!w\xb2\x880x\xb6\xdc4l\xa1\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xaa?)`\x1a\x131t^\x05\xc4(0\xa1^q\x93\x8ab7\x89\\(=A\x03\x94\x10\x00\x00\xe0\x94\xaaG\xa4\xff\xc9y622\u025b\x99\xfa\xda\x0f'4\xb0\xae\xee\x8a\x01\xb8H\x9d\xf4\xdb\xff\x94\x00\x00\u07d4\xaaI=?O\xb8fI\x1c\xf8\xf8\x00\xef\xb7\xe22N\xd7\xcf\xe5\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xaaV\xa6]\u012b\xb7/\x11\xba\xe3+o\xbb\aDG\x91\xd5\u0249(\x94\xe9u\xbfIl\x00\x00\xe0\x94\xaaZ\xfc\xfd\x83\t\xc2\u07dd\x15\xbe^jPN}pf$\u014a\x01<\xf4\"\xe3\x05\xa17\x80\x00\u07d4\xaa\x8e\xb0\x82;\a\xb0\xe6\xd2\n\xad\xda\x0e\x95\xcf85\xbe\x19.\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xaa\x91#~t\r%\xa9/\u007f\xa1F\xfa\xa1\x8c\xe5m\xc6\xe1\xf3\x892$\xf4'#\xd4T\x00\x00\u07d4\xaa\x96\x0e\x10\xc5#\x91\xc5N\x158|\xc6z\xf8'\xb51m\u0309lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\x9b\xd4X\x955\xdb'\xfa+\xc9\x03\xca\x17\xd6y\xddeH\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xa8\xde\xfe\x11\xe3a?\x11\x06\u007f\xb9\x83bZ\b\x99Z\x8d\xfc\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xaa\xe6\x8b2\x14\x02\xc8\xeb\xc14h\xf3A\xc6<\f\xf0?\u0389Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xaa\xad\x1b\xaa\xdeZ\xf0N+\x17C\x9e\x93Y\x87\xbf\x8c+\xb4\xb9\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xaa\xb0\n\xbfX(\xd7\xeb\xf2kG\u03ac\u0378\xba\x032Qf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaa\xbd\xb3\\\x15\x14\x98J\x03\x92\x13y?3E\xa1h\xe8\x1f\xf1\x89\x10\xca\u0216\xd29\x00\x00\x00\u07d4\xaa\xca`\xd9\xd7\x00\u7156\xbb\xbb\xb1\xf1\xe2\xf7\x0fF'\xf9\u060965\xbbw\xcbK\x86\x00\x00\u07d4\xaa\xce\u0629V;\x1b\xc3\x11\xdb\xdf\xfc\x1a\xe7\xf5u\x19\xc4D\f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\u04b7\xf8\x10f\x95\a\x8el\x13\x8e\xc8\x1at\x86\xaa\xca\x1e\xb2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaa\xe6\x1eC\xcb\r\f\x96\xb3\x06\x99\xf7~\x00\xd7\x11\u0423\x97\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaa\xe72\xed\xa6Y\x88\u00e0\f\u007fG/5\x1cF;\x1c\x96\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaa\xf0#\xfe\U0009091b\xb7\x8b\xb7\xab\xc9]f\x9cP\xd5(\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xaa\xf5\xb2\a\xb8\x8b\r\xe4\xac@\xd7G\xce\xe0n\x17-\xf6\xe7E\x8a\x06\xa7\xb7\x1d\u007fQ\u0410\x00\x00\u07d4\xaa\xf9\xeeK\x88lm\x1e\x95Io\xd2t#[\xf4\xec\xfc\xb0}\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xaa\xfb{\x01:\xa1\xf8T\x1c~2{\xf6P\xad\xbd\x19L \x8f\x89I\x9e\t-\x01\xf4x\x00\x00\xe0\x94\xab\t\x863\xee\xee\f\xce\xfd\xf62\xf9WTV\xf6\u0740\xfc\x86\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xab\f\xedv.\x16a\xfa\xe1\xa9*\xfb\x14\b\x88\x94\x13yH%\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab\x14\xd2!\xe3=TF)\x19\x8c\u0416\xedc\u07e2\x8d\x9fG\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xab \x9f\u0729y\u0426G\x01\n\xf9\xa8\xb5/\xc7\xd2\r\x8c\u044a\x01\xee\xe2S,|-\x04\x00\x00\u07d4\xab'\xbax\xc8\xe5\xe3\xda\xef1\xad\x05\xae\xf0\xff\x03%r\x1e\b\x89\x19^\xce\x00n\x02\xd0\x00\x00\u07d4\xab(q\xe5\a\u01fe9eI\x8e\x8f\xb4b\x02Z\x1a\x1cBd\x89*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xab8a\"o\xfe\xc1(\x91\x87\xfb\x84\xa0\x8e\xc3\xed\x042d\xe8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab=\x86\xbc\x82\x92~\f\xd4!\xd1F\xe0\u007f\x91\x93'\xcd\xf6\xf9\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xab>b\xe7z\x8b\"^A\x15\x92\xb1\xaf0\aR\xfeA$c\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xab>x)K\xa8\x86\xa0\xcf\xd5\xd3H\u007f\xb3\xa3\a\x8d3\x8dn\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xab@\x04\xc0@?~\xab\xb0\xeaXo!!V\xc4 =g\xf1\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xabAo\xe3\rX\xaf\xe5\xd9EL\u007f\xce\u007f\x83\v\xccu\x03V\x89\x0657\x01\xc6\x05\u06c0\x00\u07d4\xabEr\xfb\xb1\xd7+W]i\xecj\xd1s3\x87>\x85R\xfc\x89lj\xc5L\xdahG\x00\x00\u07d4\xabZy\x01av2\ts\xe8\xcd8\xf67U0\x02%1\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab]\xfc\x1e\xa2\x1a\xdcB\u03cc?n6\x1e$?\xd0\xdaa\xe5\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xabke\xea\xb8\xdf\xc9\x17\xec\x02Q\xb9\xdb\x0e\u03e0\xfa\x03(I\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xabp\x91\x93.K\u00dd\xbbU#\x80\u0293O\xd7\x16m\x1en\x89\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xabt\x16\xff2%IQ\u02fcbN\xc7\xfbE\xfc~\u02a8r\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xab|B\xc5\xe5-d\x1a\a\xadu\t\x9cb\x92\x8b\u007f\x86b/\x89\x126\x1a\xa2\x1d\x14\xba\x00\x00\u07d4\xab}T\xc7\xc6W\x0e\xfc\xa5\xb4\xb8\xcep\xf5*Ws\xe5\xd5;\x89\x0f(:\xbe\x9d\x9f8\x00\x00\u07d4\xab~\v\x83\xed\x9aBLm\x1ejo\x87\xa4\xdb\xf0d\t\xc7\u0589\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xab\x84\xa0\xf1G\xad&T\x00\x00+\x85\x02\x9aA\xfc\x9c\xe5\u007f\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab\x93\xb2n\xce\n\n\xa2\x13e\xaf\xed\x1f\xa9\xae\xa3\x1c\xd5Dh\x89W+{\x98sl \x00\x00\u07d4\xab\x94\x8aJ\xe3y\\\xbc\xa11&\xe1\x92S\xbd\xc2\x1d:\x85\x14\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xab\x9a\xd3n\\t\xce.\x969\x9fW\x83\x941\xd0\u77d6\xab\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xab\xb2\xe6\xa7*@\xban\xd9\b\u037c\xec<V\x12X12\xfe\x89O%\x91\xf8\x96\xa6P\x00\x00\u07d4\xab\xc0h\xb4\x97\x9b\x0e\xa6Jb\u04f7\xaa\x89}s\x81\r\xc53\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xab\xc4_\x84\xdbs\x82\xdd\xe5L_}\x898\xc4/O:;\u0109\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xab\xc4\xca\xebGMF'\xcbn\xb4V\xec\xba\x0e\xcd\b\xed\x8a\xe1\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xab\xc7G\x06\x96I`\xdf\xe0\u0723\u0727\x9e\x92\x16\x05o\x1c\xf4\x8a\bxg\x83&\xea\xc9\x00\x00\x00\xe0\x94\xab\u0269\x9e\x8a!H\xa5Zm\x82\xbdQ\xb9\x8e\xb59\x1f\u06ef\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xab\u037c\x8f\x1d\xd1:\xf5x\u0524wJb\x18+\xed\xf9\xf9\xbe\x89\x01\xfc\xc2{\xc4Y\xd2\x00\x00\u07d4\xab\xd1T\x905\x13\xb8\xdaO\x01\x9fh(K\x06V\xa1\xd0\x16\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xab\xd2\x1e\xff\x95O\u01a7\xde&\x91*|\xbb0:f\a\x80N\x89R<\x9a\xa6\x96\xeb\x94\x00\x00\u07d4\xab\xd4\xd6\xc1fcX\xc0@o\xdf:\xf2H\xf7\x8e\u0383\x01\x04\x89r}\xe3J$\xf9\x00\x00\x00\u07d4\xab\xd9`[>\x91\xac\xfdwx0\xd1dcG\x8a\xe0\xfcw \x89\a?u\u0460\x85\xba\x00\x00\xe0\x94\xab\u071f\x1b\xcfM\x19\xee\x96Y\x100\xe7r\xc340/}\x83\x8a\b~^\x11\xa8\x1c\xb5\xf8\x00\x00\u07d4\xab\xde\x14{*\xf7\x89\ua946T~f\xc4\xfa&d\xd3(\xa4\x89\rk`\x81\xf3L\x12\x80\x00\xe0\x94\xab\xe0|\xedj\xc5\xdd\xf9\x91\xef\xf6\xc3\xda\"jt\x1b\xd2C\xfe\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xab\xf1/\xa1\x9e\x82\xf7lq\x8f\x01\xbd\xca\x00\x03gE#\xef0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xab\xf7(\u03d3\x12\xf2!(\x02NpF\xc2Q\xf5\xdcY\x01\xed\x8a\x06A\xe8\xa15c\xd8\xf8\x00\x00\u07d4\xab\xf8\xff\xe0p\x8a\x99\xb5(\xcc\x1e\xd4\xe9\xceK\r\x060\xbe\x8c\x89z\xb5\u00ae\xee\xe68\x00\x00\u07d4\xab\xfc\xf5\xf2P\x91\xceW\x87_\xc6t\xdc\xf1\x04\xe2\xa7=\xd2\xf2\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xab\xfe\x93d%\xdc\u01f7K\x95P\x82\xbb\xaa\xf2\xa1\x1dx\xbc\x05\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xac\x02OYO\x95X\xf0ICa\x8e\xb0\xe6\xb2\xeeP\x1d\xc2r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\x12*\x03\xcd\x05\x8c\x12._\xe1{\x87/Hw\xf9\u07d5r\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xac\x14.\xda\x11W\xb9\xa9\xa6C\x90\xdf~j\xe6\x94\xfa\u0249\x05\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\x1d\xfc\x98Kq\xa1\x99)\xa8\x1d\x81\xf0J|\xbb\x14\a7\x03\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xac!\xc1\xe5\xa3\xd7\xe0\xb5\x06\x81g\x9d\xd6\u01d2\xdb\u0287\xde\u02ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\xe0\x94\xac(\x89\xb5\x96o\f\u007f\x9e\xdbB\x89\\\xb6\x9d\x1c\x04\xf9#\xa2\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xac(\xb5\xed\xea\x05\xb7o\x8c_\x97\bEA'|\x96ijL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac,\x8e\t\xd0d\x93\xa68XC{\xd2\v\xe0\x19bE\x03e\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xac.vm\xac?d\x8fcz\xc6q?\u0770h\xe4\xa4\xf0M\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xac9\x00)\x8d\xd1M|\xc9mJ\xbbB\x8d\xa1\xba\xe2\x13\xff\xed\x8a\x05<\xa1)t\x85\x1c\x01\x00\x00\u07d4\xac=\xa5&\xcf\u0388)s\x02\xf3LI\xcaR\r\xc2q\xf9\xb2\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xacD`\xa7nm\xb2\xb9\xfc\xd1R\xd9\xc7q\x8d\x9a\xc6\xed\x8co\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xacJ\xcf\xc3n\xd6\tJ'\xe1\x18\xec\xc9\x11\xcdG>\x8f\xb9\x1f\x89a\x91>\x14@<\f\x00\x00\u07d4\xacL\xc2V\xaet\xd6$\xac\xe8\r\xb0x\xb2 \u007fW\x19\x8fk\x89lyt\x12?d\xa4\x00\x00\u07d4\xacN\xe9\xd5\x02\xe7\xd2\xd2\xe9\x9eY\xd8\xca}_\x00\xc9KM\u058965\u026d\xc5\u07a0\x00\x00\u07d4\xacR\xb7~\x15fH\x14\xf3\x9eO'\x1b\xe6A0\x8d\x91\xd6\u0309\v\xed\x1d\x02c\xd9\xf0\x00\x00\u07d4\xacY\x99\xa8\x9d-\u0486\u0568\fm\xee~\x86\xaa\xd4\x0f\x9e\x12\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xac_br1H\r\r\x950.m\x89\xfc2\xcb\x1dO\xe7\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xac`\x8e+\xac\x9d\xd2\a(\u0494~\xff\xbb\xbf\x90\n\x9c\xe9K\x8a\x01EK\r\xb3uh\xfc\x00\x00\u07d4\xacm\x02\xe9\xa4k7\x9f\xacJ\u0271\u05f5\xd4{\xc8P\xce\x16\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xacoh\xe87\xcf\x19a\xcb\x14\xabGDm\xa1h\xa1m\u0789\x89Hz\x9a0E9D\x00\x00\u07d4\xacw\xbd\xf0\x0f\u0558[]\xb1+\xbe\xf8\x008\n\xbc*\x06w\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xac~\x03p'#\xcb\x16\xee'\xe2-\u0438\x15\xdc-\\\xae\x9f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xac\x8bP\x9a\xef\xea\x1d\xbf\xaf+\xb35\x00\xd6W\vo\xd9mQ\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xac\x8e\x87\xdd\xda^x\xfc\xbc\xb9\xfa\u007f\xc3\xce\x03\x8f\x9f}.4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xac\x9f\xffh\xc6\x1b\x01\x1e\xfb\xec\xf08\xedr\u06d7\xbb\x9er\x81\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xac\xa1\xe6\xbcd\xcc1\x80\xf6 \xe9M\u0171\xbc\xfd\x81X\xe4]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xa2\xa883\v\x170-\xa71\xd3\r\xb4\x8a\x04\xf0\xf2\a\xc1\x89Hz\x9a0E9D\x00\x00\u07d4\xac\xaa\xdd\xcb\xf2\x86\xcb\x0e!]\xdaUY\x8f\u007f\xf0\xf4\xad\xa5\u018965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xb9C8UK\u0108\u0308\xae-\x9d\x94\b\rk\u07c4\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xac\xbc-\x19\xe0l;\xab\xbb[o\x05+k\xf7\xfc7\xe0r)\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xbd\x18U\x89\xf7\xa6\x8ag\xaaK\x1b\xd6Pw\xf8\xc6NN!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xac\xc0bp,Ya]4D\xefb\x14\xb8\x86+\x00\x9a\x02\xed\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xac\xc0\x90\x9f\xda.\xa6\xb7\xb7\xa8\x8d\xb7\xa0\xaa\xc8h\t\x1d\xdb\xf6\x89\x013v_\x1e&\u01c0\x00\u07d4\xac\xc1\u01c7\x86\xabM+;'q5\xb5\xba\x12>\x04\x00Hk\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xac\xc4j*U\\t\xde\u0522\xbd\tN\x82\x1b\x97\x84;@\xc0\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xac\u015f;0\xce\xff\xc5da\xcc[\x8d\xf4\x89\x02$\x0e\x0e{\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\xce\x01\xe0\xa7\x06\x10\xdcp\xbb\x91\xe9\x92o\xa9\x95\u007f7/\xba\x89\x1d\x1c_>\xda \xc4\x00\x00\u07d4\xac\xd8\u0751\xf7\x14vLEg|c\xd8R\xe5n\xb9\xee\xce.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xac\u2af6;\x06\x04@\x9f\xbd\xe3\xe7\x16\u0487mD\xe8\xe5\u0749\b=lz\xabc`\x00\x00\xe0\x94\xac\xec\x91\xefiA\xcfc\v\xa9\xa3\u71e0\x12\xf4\xa2\xd9\x1d\u050a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xad\nJ\xe4x\xe9cn\x88\xc6\x04\xf2B\xcfT9\xc6\xd4V9\x89\xbe\xd1\xd0&=\x9f\x00\x00\x00\u07d4\xad\x17\x99\xaa\xd7`+E@\u0343/\x9d\xb5\xf1\x11P\xf1hz\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\x1dh\xa08\xfd%\x86\x06~\xf6\xd15\xd9b\x8ey\xc2\xc9$\x89\xfe\t\xa5'\x9e*\xbc\x00\x00\u07d4\xad*\\\x00\xf9#\xaa\xf2\x1a\xb9\xf3\xfb\x06n\xfa\n\x03\xde/\xb2\x8965\xbbw\xcbK\x86\x00\x00\u07d4\xad5e\xd5+h\x8a\xdd\xed\b\x16\x8b-8r\xd1}\n&\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xad7|\xd2^\xb5>\x83\xae\t\x1a\n\x1d+E\x16\xf4\x84\xaf\u0789i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xadAM)\xcb~\xe9s\xfe\xc5N\"\xa3\x88I\x17\x86\xcfT\x02\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xadD5~\x01~$OGi1\u01f8\x18\x9e\xfe\xe8\n]\n\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xadW\xaa\x9d\x00\xd1\fC\x9b5\xef\xcc\v\xec\xac.9U\xc3\x13\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xadY\xa7\x8e\xb9\xa7J\u007f\xbd\xae\xfa\xfa\x82\xea\u0684u\xf0\u007f\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xadZ\x8d<dx\xb6\x9fe}\xb3\x83z%u\xef\x8e\x1d\xf91\x89\x02\x01V\xe1\x04\xc1\xb3\x00\x00\u07d4\xadf\r\xec\x82U\"\xa9\xf6/\xce\xc3\u01771\x98\r\u0086\ua262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xadf(5.\xd39\v\xaf\xa8m\x92>V\x01L\xfc\xb3`\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xadr\x81!\x87?\x04V\xd0Q\x8b\x80\xabe\x80\xa2\x03pe\x95\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xads,\x97e\x93\xee\xc4x;N.\xcdy9yx\v\xfe\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xad}\xd0S\x85\x9e\xdf\xf1\xcbo\x9d*\xcb\xedm\xd5\xe32Bo\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xad\x80\xd8e\xb8\\4\xd2\xe6IK.z\xef\xeak\x9a\xf1\x84\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xad\x8b\xfe\xf8\u018aH\x16\xb3\x91o5\xcb{\xfc\xd7\xd3\x04\tv\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xad\x8eH\xa3wi]\xe0\x146:R:(\xb1\xa4\fx\xf2\b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xad\x91\n#\u0585\x06\x13eJ\xf7\x863z\u04a7\bh\xacm\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xad\x92~\x03\xd1Y\x9ax\xca+\xf0\xca\u04a1\x83\xdc\xebq\xea\xc0\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xad\x92\xca\x06n\xdb|q\x1d\xfc[\x16a\x92\xd1\xed\xf8\xe7q\x85\x8a\a\x9f\x90\\o\xd3N\x80\x00\x00\u07d4\xad\x94#_\u00f3\xf4z$\x13\xaf1\u8111I\b\xef\fE\x89\x1b\x1b\x01B\xd8\x15\x84\x00\x00\u07d4\xad\x9e\x97\xa0H/5:\x05\xc0\xf7\x92\xb9w\xb6\xc7\xe8\x11\xfa_\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xad\x9fL\x89\n;Q\x1c\xeeQ\xdf\xe6\xcf\xd7\xf1\t;vA,\x89\x1bv|\xbf\xeb\f\xe4\x00\x00\u07d4\xad\xaa\x0eT\x8c\x03Z\xff\xedd\xcag\x8a\x96?\xab\xe9\xa2k\xfd\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xad\xb9H\xb1\xb6\xfe\xfe }\xe6^\x9b\xbc-\xe9\x8e`]\vW\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xad\xc1\x9e\xc85\xaf\xe3\u5347\u0713\xa8\xa9!<\x90E\x13&\x89j\xdb\xe54\"\x82\x00\x00\x00\u07d4\xad\xc8\"\x8e\xf9(\xe1\x8b*\x80}\x00\xfb<ly\xcd\x1d\x9e\x96\x89\x01<i\xdf3N\xe8\x00\x00\u07d4\xad\xdb&1r'\xf4\\\x87\xa2\u02d0\xdcL\xfd\x02\xfb#\xca\xf8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xad\xe6\xf8\x16;\xf7\u01fbJ\xbe\x8e\x98\x93\xbd\f\xc1\x12\xfe\x88r\x89\x11\xc2]\x00M\x01\xf8\x00\x00\u07d4\xad\xeb J\xa0\u00ce\x17\x9e\x81\xa9N\u0633\xe7\xd50G\xc2k\x89 \xf5\xb1\uab4d\x80\x00\x00\u07d4\xad\xebR\xb6\x04\xe5\xf7\u007f\xaa\xac\x88'[\x8dkI\xe9\xf9\xf9\u007f\x89qBk\x00\x95n\xd2\x00\x00\xe0\x94\xad\xf1\xac\xfe\x99\xbc\x8c\x14\xb3\x04\xc8\xd9\x05\xba'e{\x8a{\u010a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xad\xf8R\x03\xc87j_\u0798\x158J5\f8y\xc4\u02d3\x89>1\xfcgX\x15\xaa\x00\x00\u07d4\xad\xff\r\x1d\v\x97G\x1ev\u05c9\xd2\u470at\xf9\xbdT\xff\x89e\xea=\xb7UF`\x00\x00\u07d4\xae\x06,D\x86\x18d0u\xdez\x0004-\xce\xd6=\xba\u05c9,\xc6\u034c\u0082\xb3\x00\x00\xe0\x94\xae\x10\xe2z\x01O\r0k\xaf&mH\x97\u021a\xee\xe2\xe9t\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xae\x12k8,\xf2W\xfa\xd7\xf0\xbc}\x16)~T\xccrg\u0689\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x13\xa0\x85\x11\x11\x0f2\xe5;\xe4\x12xE\xc8C\xa1\xa5|{\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xae\x17\x9aF\r\xb6c&t=$\xe6u#\xa5{$m\xaf\u007f\x8a\x01\x00\a\xae|\xe5\xbb\xe4\x00\x00\u07d4\xae\"(ey\x90y\xaa\xf4\xf0gJ\f\u06ab\x02\xa6\xd5p\xff\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xae#\x9a\xcf\xfdN\xbe.\x1b\xa5\xb4\x17\x05r\xdcy\xcce3\xec\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xae/\x9c\x19\xacv\x13e\x94C#\x93\xb0G\x1d\b\x90!d\u04c9%\xdf\x05\u01a8\x97\xe4\x00\x00\u07d4\xae4\x86\x1d4\"S\x19O\xfcfR\xdf\xdeQ\xabD\xca\xd3\xfe\x89\x19F\bhc\x16\xbd\x80\x00\u07d4\xae6\xf7E!!\x91>\x80\x0e\x0f\xcd\x1ae\xa5G\x1c#\x84o\x89\b\xe3\xf5\v\x17<\x10\x00\x00\u07d4\xae?\x98\xa4C\xef\xe0\x0f>q\x1dR]\x98\x94\u071aa\x15{\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xaeG\xe2`\x9c\xfa\xfe6\x9df\xd4\x15\xd99\xde\x05\b\x1a\x98r\x8a\x05\xba\xec\xf0%\xf9\xb6P\x00\x00\u07d4\xaeO\x12.5\xc0\xb1\xd1\xe4\x06\x92\x91E|\x83\xc0\u007f\x96_\xa3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaePU\x81L\xb8\xbe\f\x11{\xb8\xb1\xc8\u04b6;F\x98\xb7(\x89\x01\xbc\x93.\xc5s\xa3\x80\x00\u07d4\xaeS\x8cs\u0173\x8d\x8dXM~\xbd\xad\xef\xb1\\\xab\xe4\x83W\x896'\xe8\xf7\x127<\x00\x00\u07d4\xaeW\xcc\x12\x9a\x96\xa8\x99\x81\xda\xc6\r/\xfb\x87}]\xc5\xe42\x89<:#\x94\xb3\x96U\x00\x00\u07d4\xaeZ\xa1\xe6\u00b6\x0fo\xd3\xef\xe7!\xbbJq\x9c\xbe=o]\x89+$\u01b5Z^b\x00\x00\u07d4\xae\\\x9b\xda\xd3\xc5\u0221\"\x04D\xae\xa5\xc2)\xc1\x83\x9f\x1dd\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xae\\\xe35Z{\xa9\xb32v\f\tP\u00bcE\xa8_\xa9\xa0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xae]\"\x1a\xfc\xd3\u0493U\xf5\b\xea\xdf\xca@\x8c\xe3<\xa9\x03\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xaec[\xf781\x11\x9d-)\xc0\xd0O\xf8\xf8\xd8\u0425zF\x89Hz\x9a0E9D\x00\x00\xe0\x94\xaed\x81U\xa6X7\x0f\x92\x9b\xe3\x84\xf7\xe0\x01\x04~I\xddF\x8a\x02\xdf$\xae2\xbe D\x00\x00\xe0\x94\xaeo\fs\xfd\xd7|H\x97'Q!t\u0675\x02\x96a\x1cL\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xaep\xe6\x9d,J\n\xf8\x18\x80{\x1a'\x05\xf7\x9f\u0435\xdb\u01095e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xaew9\x12N\xd1S\x05%\x03\xfc\x10\x14\x10\xd1\xff\xd8\xcd\x13\xb7\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xaex\xbb\x84\x919\xa6\xba8\xae\x92\xa0\x9ai`\x1c\xc4\xcbb\u0449\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xae\x84\"\x10\xf4M\x14\u0124\u06d1\xfc\x9d;;P\x01O{\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xae\x84.\x81\x85\x8e\xcf\xed\xf6Plhm\xc2\x04\xac\x15\xbf\x8b$\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xae\x89T\xf8\xd6\x16m\xe5\a\xcfa)}\x0f\xc7\xcak\x9eq(\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xae\x9e\xcdk\u0755.\xf4\x97\xc0\x05\n\u0aca\x82\xa9\x18\x98\u0389\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\xae\x9f\\?\xbb\xe0\u027c\xbf\x1a\xf8\xfft\xea(\v:]\x8b\b\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xae\xad\x88\u0589Ak\x1c\x91\xf26D!7[}<p\xfb.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xae\xad\xfc\u0417\x8e\xda\xd7J2\xbd\x01\xa0\xa5\x1d7\xf2F\xe6a\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xae\xb9\x16\xeb\xf4\x9d\x0f\x86\xc1?s1\xce\xf1\x9e\x12\x997Q-\x89 \x85e[\x8d\x1b\n\x00\x00\u07d4\xae\xbdO ]\u7676K5d\xb2V\xd4*q\x1d7\uf649?\u03cbEt\xf8N\x00\x00\xe0\x94\xae\xc2|\xe2\x13>\x82\xd0RR\n\xfb\\Wm\x9f~\xb9>\u048a\r\xd0A \xba\t\xcf\xe6\x00\x00\u07d4\xae\xc2\u007f\xf5\xd7\xf9\xdd\u0691\x18?F\xf9\xd5%C\xb6\xcd+/\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xae\xe4\x9dh\xad\xed\xb0\x81\xfdCpZ_x\xc7x\xfb\x90\xdeH\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xae\xf5\xb1\"X\xa1\x8d\xec\a\xd5\xec.1et\x91\x9dy\xd6\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xae\xfc\xfe\x88\xc8&\xcc\xf11\xd5N\xb4\ua7b8\x0ea\xe1\xee%\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xaf\x06\xf5\xfam\x12\x14\xecC\x96}\x1b\xd4\xdd\xe7J\xb8\x14\xa98\x89\x04\xc5>\xcd\xc1\x8a`\x00\x00\u07d4\xaf\x11H\xefl\x8e\x10=u0\xef\xc9\x16y\u026c'\x00\t\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xaf >\"\x9d~mA\x9d\xf47\x8e\xa9\x87\x15Q_c\x14\x85\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xaf X\xc7(,\xf6|\x8c<\xf90\x13<\x89a|\xe7])\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\xaf&\xf7\u01bfE> x\xf0\x89S\u4c80\x04\xa2\xc1\xe2\t\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xaf0\x87\xe6.\x04\xbf\x90\rZT\xdc>\x94bt\u0692B;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf6\x14\u0736\x8a6\xe4ZN\x91\x1ebybG\"-Y[\x89z\x81\x06_\x11\x03\xbc\x00\x00\u07d4\xaf6\x15\u01c9\u0431\x15*\xd4\xdb%\xfe]\xcf\"(\x04\xcfb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xaf<\xb5\x96Y3\xe7\xda\u0603i;\x9c>\x15\xbe\xb6\x8aHs\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xafD\x93\xe8R\x1c\xa8\x9d\x95\xf5&|\x1a\xb6?\x9fEA\x1e\x1b\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xafL\xf4\x17\x85\x16\x1fW\x1d\f\xa6\x9c\x94\xf8\x02\x1fA)N\u028a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xafR\x9b\xdbE\x9c\xc1\x85\xbe\xe5\xa1\u014b\xf7\xe8\xcc\xe2\\\x15\r\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xafg\xfd>\x12\u007f\xd9\xdc6\xeb?\xcdj\x80\u01feOu2\xb2\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xafw\x1094Z40\x01\xbc\x0f\x8aY#\xb1&\xb6\rP\x9c\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xaf\u007fy\xcbAZ\x1f\xb8\u06fd\tF\a\xee\x8dA\xfb|Z;\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xaf\x87\xd27\x1e\xf3x\x95\u007f\xbd\x05\xba/\x1df\x93\x1b\x01\u2e09%\xf2s\x93=\xb5p\x00\x00\u07d4\xaf\x88\x0f\xc7V}U\x95\xca\xcc\xe1\\?\xc1L\x87B\xc2l\x9e\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xaf\x8e\x1d\xcb1L\x95\r6\x87CM0\x98X\xe1\xa8s\x9c\u0509\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xaf\x99-\xd6i\xc0\x88>U\x15\xd3\xf3\x11*\x13\xf6\x17\xa4\xc3g\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xa1\u056d8\xfe\xd4GY\xc0[\x89\x93\xc1\xaa\r\xac\xe1\x9f@\x89\x04V9\x18$O@\x00\x00\xe0\x94\xaf\xa59XnG\x19\x17J;F\xb9\xb3\xe6c\xa7\u0475\xb9\x87\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xa6\x94n\xff\xd5\xffS\x15O\x82\x01\x02S\xdfG\xae(\f\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xaf\xc8\xeb\u860b\xd4\x10Z\xccL\x01\x8eTj\x1e\x8f\x9cx\x88\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xaf\xcc}\xbb\x83V\xd8B\xd4:\xe7\xe2<\x84\"\xb0\"\xa3\b\x03\x8a\x06o\xfc\xbf\xd5\xe5\xa3\x00\x00\x00\u07d4\xaf\xd0\x19\xff6\xa0\x91U4ki\x97H\x15\xa1\xc9\x12\xc9\n\xa4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xaf\xda\xc5\xc1\xcbV\xe2E\xbfp3\x00f\xa8\x17\uabecL\u0449\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xdd\x1bxab\xb81~ \xf0\xe9y\xf4\xb2\xceHmv]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xaf\xf1\x04Z\xdf'\xa1\xaa2\x94a\xb2M\xe1\xba\u950ai\x8b\x89\x01\u03c4\xa3\n\n\f\x00\x00\u07d4\xaf\xf1\a\x96\v~\xc3N\u0590\xb6e\x02M`\x83\x8c\x19\x0fp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xaf\xf1\x1c\xcfi\x93\x04\xd5\xf5\x86*\xf8`\x83E\x1c&\xe7\x9a\xe5\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xaf\xf1at\nm\x90\x9f\xe9\x9cY\xa9\xb7yE\xc9\x1c\xc9\x14H\x89\x03@\xaa\xd2\x1b;p\x00\x00\xe0\x94\xaf\xfc\x99\xd5\ubd28O\xe7x\x8d\x97\xdc\xe2t\xb08$\x048\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xaf\xfe\xa0G7\"\xcb\u007f\x0e\x0e\x86\xb9\xe1\x18\x83\xbfB\x8d\x8dT\x89i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xb0\t\x96\xb0Vn\xcb>rC\xb8\"y\x88\u0733R\xc2\x18\x99\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xb0\x1e8\x9b(\xa3\x1d\x8eI\x95\xbd\xd7\xd7\xc8\x1b\xee\xab\x1eA\x19\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0-\x06(s3EE\u03a2\x92\x18\xe4\x05w`Y\x0ft#\x89\xac\xb6\xa1\xc7\xd9:\x88\x00\x00\u07d4\xb0/\xa2\x93\x87\xec\x12\xe3\u007fi\"\xacL\xe9\x8c[\t\xe0\xb0\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb06\x91k\xda\u03d4\xb6\x9eZ\x8ae`)u\xeb\x02a\x04\u0749\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb0A1\x0f\xe9\xee\u0586L\xed\u053e\xe5\x8d\xf8\x8e\xb4\xed<\xac\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb0U\xafL\xad\xfc\xfd\xb4%\xcfe\xbad1\a\x8f\a\xec\u056b\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0W\x11S\xdb\x1cN\u05ec\xae\xfe\x13\xec\xdf\xdbr\xe7\xe4\xf0j\x8a\x11\f\xffyj\xc1\x95 \x00\x00\u07d4\xb0n\xab\t\xa6\x10\u01a5=V\xa9F\xb2\xc44\x87\xac\x1d[-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb0rI\xe0U\x04J\x91U5\x9a@)7\xbb\xd9T\xfeH\xb6\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xb0v\x182\x8a\x90\x13\a\xa1\xb7\xa0\xd0X\xfc\xd5xn\x9er\xfe\x8a\x06gI]JC0\xce\x00\x00\u07d4\xb0y\xbbM\x98f\x14:m\xa7*\xe7\xac\x00\"\x06)\x811\\\x89)3\x1eeX\xf0\xe0\x00\x00\u07d4\xb0{\xcc\bZ\xb3\xf7)\xf2D\x00Ah7\xb6\x996\xba\x88s\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xb0{\xcf\x1c\xc5\xd4F.Q$\xc9e\xec\xf0\xd7\r\xc2z\xcau\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xb0|\xb9\xc1$\x05\xb7\x11\x80uC\u0113De\xf8\u007f\x98\xbd-\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb0\u007f\u07af\xf9\x1dD`\xfel\xd0\u8870\xbd\x8d\"\xa6.\x87\x8a\x01\x1d%)\xf3SZ\xb0\x00\x00\xe0\x94\xb0\x9f\xe6\xd44\x9b\x99\xbc7\x93\x80T\x02-T\xfc\xa3f\xf7\xaf\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\xe0\x94\xb0\xaa\x00\x95\f\x0e\x81\xfa2\x10\x17>r\x9a\xaf\x16:'\xcdq\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb0\xacN\xfff\x80\xee\x14\x16\x9c\xda\xdb\xff\xdb0\x80Om%\xf5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb0\xb3j\xf9\xae\xee\u07d7\xb6\xb0\"\x80\xf1\x14\xf19\x84\xea2`\x895e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xb0\xb7y\xb9K\xfa<.\x1fX{\u031c~!x\x92\"7\x8f\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xb0\xba\xeb0\xe3\x13wlLm$t\x02\xbaAg\xaf\u0361\u0309j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb0\xbb)\xa8a\xea\x1dBME\xac\u053f\u0112\xfb\x8e\xd8\t\xb7\x89\x04V9\x18$O@\x00\x00\xe0\x94\xb0\xc1\xb1w\xa2 \xe4\x1f|t\xd0|\u0785i\xc2\x1cu\xc2\xf9\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xb0\xc7\xceL\r\xc3\u00bb\xb9\x9c\xc1\x85{\x8aE_a\x17\x11\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb0\xce\xf8\xe8\xfb\x89\x84\xa6\x01\x9f\x01\xc6y\xf2r\xbb\xe6\x8f\\w\x89\b=lz\xabc`\x00\x00\xe0\x94\xb0\xd3+\xd7\xe4\u6577\xb0\x1a\xa3\xd0Ao\x80U}\xba\x99\x03\x8a\x03s\x9f\xf0\xf6\xe6\x130\x00\x00\xe0\x94\xb0\xd3\u0247+\x85\x05n\xa0\xc0\xe6\xd1\xec\xf7\xa7~<\u6ac5\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xb0\xe4i\u0206Y8\x15\xb3IV8Y]\xae\xf0f_\xaeb\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xb0\xe7`\xbb\a\xc0\x81wsE\xe0W\x8e\x8b\u0218\"mN;\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\x040\x04\xec\x19A\xa8\xcfO+\x00\xb1W\x00\u076co\xf1~\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\x05\xdd=\x98|\xff\xd8\x13\xe9\xc8P\n\x80\xa1\xad%}V\u0189lj\xccg\u05f1\xd4\x00\x00\u07d4\xb1\x0f\u04a6G\x10/\x88\x1ft\xc9\xfb\xc3}\xa62\x94\x9f#u\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb1\x15\xee:\xb7d\x1e\x1a\xa6\xd0\x00\xe4\x1b\xfc\x1e\xc7!\f/2\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xb1\x17\x8a\xd4s\x83\xc3\x1c\x814\xa1\x94\x1c\xbc\xd4t\xd0bD\xe2\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb1\x17\x95\x89\u1779\xd4\x15W\xbb\xec\x1c\xb2L\xcc-\xec\x1c\u007f\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb1\x19\u76a9\xb9\x16Re\x81\xcb\xf5!\xefGJ\xe8M\xcf\xf4\x89O\xba\x10\x01\xe5\xbe\xfe\x00\x00\u07d4\xb1\x1f\xa7\xfb'\n\xbc\xdfZ.\xab\x95\xaa0\u013566\uffc9+^:\xf1k\x18\x80\x00\x00\u07d4\xb1$\xbc\xb6\xff\xa40\xfc\xae.\x86\xb4_'\xe3\xf2\x1e\x81\xee\b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1)\xa5\xcbq\x05\xfe\x81\v\u0615\xdcr\x06\xa9\x91\xa4TT\x88\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\xe0\x94\xb1.\xd0{\x8a8\xadU\x066?\xc0z\vmy\x996\xbd\xaf\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb14\xc0\x049\x1a\xb4\x99(x3zQ\xec$/B(WB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1?\x93\xaf0\xe8\xd7fs\x81\xb2\xb9[\xc1\xa6\x99\xd5\xe3\xe1)\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\xb1E\x92\x85\x86>\xa2\xdb7Y\xe5F\u03b3\xfb7a\xf5\x90\x9c\x89<\xd7*\x89@\x87\xe0\x80\x00\u07d4\xb1F\xa0\xb9%U<\xf0o\xca\xf5J\x1bM\xfe\xa6!)\aW\x89lnY\xe6|xT\x00\x00\xe0\x94\xb1Jz\xaa\x8fI\xf2\xfb\x9a\x81\x02\u05bb\xe4\u010a\xe7\xc0o\xb2\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1K\xbe\xffpr\tu\xdca\x91\xb2\xa4O\xf4\x9f&r\x87<\x89\a\xc0\x86\x0eZ\x80\xdc\x00\x00\xe0\x94\xb1L\xc8\xde3\xd63\x826S\x9aH\x90 \xceFU\xa3+\u018a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb1M\xdb\x03\x86\xfb`c\x98\xb8\xccGVZ\xfa\xe0\x0f\xf1\xd6j\x89\xa1*\xff\b>f\xf0\x00\x00\u07d4\xb1S\xf8(\xdd\amJ|\x1c%t\xbb-\xee\x1aD\xa3\x18\xa8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1T\x0e\x94\xcf\xf3F\\\xc3\u0447\xe7\xc8\u3f6f\x98FY\u2262\x15\xe4C\x90\xe33\x00\x00\u07d4\xb1X\xdbC\xfab\xd3\x0ee\xf3\u041b\xf7\x81\u01f6sr\uba89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1ar_\xdc\xed\xd1yR\xd5{#\xef([~K\x11i\xe8\x89\x02\xb6\xdf\xed6d\x95\x80\x00\u07d4\xb1dy\xba\x8e}\xf8\xf6>\x1b\x95\xd1I\u0345)\xd75\xc2\u0689-\xe3:j\xac2T\x80\x00\u07d4\xb1f\xe3}.P\x1a\xe7<\x84\x14+_\xfbZ\xa6U\xddZ\x99\x89l]\xb2\xa4\xd8\x15\xdc\x00\x00\u07d4\xb1\x83\xeb\xeeO\xcbB\xc2 \xe4wt\xf5\x9dlT\xd5\xe3*\xb1\x89V\xf7\xa9\xc3<\x04\xd1\x00\x00\u07d4\xb1\x88\a\x84D\x02~8g\x98\xa8\xaehi\x89\x19\xd5\xcc#\r\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xb1\x89j7\xe5\u0602Z-\x01vZ\xe5\xdeb\x99w\u0783R\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb1\x8eg\xa5\x05\n\x1d\xc9\xfb\x19\t\x19\xa3=\xa88\xefDP\x14\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb1\xa2\xb4:t3\xdd\x15\v\xb8\"'\xedQ\x9c\u05b1B\u04c2\x89\x94mb\rtK\x88\x00\x00\u07d4\xb1\xc0\u040b6\xe1\x84\xf9\x95*@7\xe3\xe5:f}\a\nN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb1\xc3(\xfb\x98\xf2\xf1\x9a\xb6do\n|\x8cVo\xdaZ\x85@\x89\x87\x86x2n\xac\x90\x00\x00\xe0\x94\xb1\xc7Qxi9\xbb\xa0\xd6q\xa6w\xa1X\u01ab\xe7&^F\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xb1\xcdK\xdf\xd1\x04H\x9a\x02n\u025dYs\a\xa0By\xf1s\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb1\u03d4\xf8\t\x15\x05\x05_\x01\n\xb4\xba\u0196\xe0\xca\x0fg\xa1\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xb1\u05b0\x1b\x94\xd8T\xfe\x8b7J\xa6^\x89\\\xf2*\xa2V\x0e\x892\xf5\x1e\u06ea\xa30\x00\x00\xe0\x94\xb1\u06e5%\v\xa9bWU$n\x06yg\xf2\xad/\a\x91\u078a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xb1\xe2\u0755\xe3\x9a\xe9w\\U\xae\xb1?\x12\xc2\xfa#0S\xba\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb1\xe6\xe8\x10\xc2J\xb0H\x8d\xe9\xe0\x1eWH7\x82\x9f|w\u0409\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb1\xe9\xc5\xf1\xd2\x1eauzk.\xe7Y\x13\xfcZ\x1aA\x01\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\x03\u049elV\xb9&\x99\u0139-\x1fo\x84d\x8d\xc4\u03fc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x16\xdcY\xe2|=ry\xf5\xcd[\xb2\xbe\u03f2`n\x14\u0649\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\x1byy\xbf|\\\xa0\x1f\xa8-\xd6@\xb4\x1c9\xe6\u01bcu\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb2#\xbf\x1f\xbf\x80H\\\xa2\xb5V}\x98\xdb{\xc3SM\xd6i\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2-PU\xd9b15\x96\x1ej\xbd'<\x90\xde\xea\x16\xa3\xe7\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb2-\xad\xd7\xe1\xe0R2\xa927\xba\xed\x98\xe0\u07d2\xb1\x86\x9e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb24\x03_uDF<\xe1\xe2+\xc5S\x06F\x84\xc5\x13\xcdQ\x89\r\x89\xfa=\u010d\xcf\x00\x00\u07d4\xb2G\u03dcr\xecH*\xf3\xea\xa7Ye\x8fy=g\nW\f\x891p\x8a\xe0\x04T@\x00\x00\u07d4\xb2ghA\xee\x9f-1\xc1r\xe8#\x03\xb0\xfe\x9b\xbf\x9f\x1e\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb2y\xc7\xd3U\u0088\x03\x92\xaa\u046a!\xee\x86|;5\a\u07c9D[\xe3\xf2\uf1d4\x00\x00\u07d4\xb2|\x1a$ L\x1e\x11\x8du\x14\x9d\xd1\t1\x1e\a\xc0s\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb2\x81\x81\xa4X\xa4@\xf1\u01bb\x1d\xe8@\x02\x81\xa3\x14\x8fL5\x89\x14b\fW\xdd\xda\xe0\x00\x00\xe0\x94\xb2\x82E\x03|\xb1\x92\xf7W\x85\u02c6\xcb\xfe|\x93\r\xa2X\xb0\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xb2\x87\xf7\xf8\xd8\u00c7,\x1bXk\xcd}\n\xed\xbf~s'2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb2\x8b\xb3\x9f4fQ|\xd4o\x97\x9c\xf5\x96S\xee}\x8f\x15.\x89\x18e\x01'\xcc=\xc8\x00\x00\u07d4\xb2\x8d\xbf\xc6I\x98\x94\xf7:q\xfa\xa0\n\xbe\x0fK\xc9\u045f*\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\x96\x8f}5\xf2\b\x87\x161\xc6h{?=\xae\xab\xc6al\x89\bu\xc4\u007f(\x9fv\x00\x00\u07d4\xb2\x9f[|\x190\xd9\xf9z\x11^\x06pf\xf0\xb5M\xb4K;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb2\xa1D\xb1\xeag\xb9Q\x0f\"g\xf9\xda9\xd3\xf9=\xe2fB\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xa2\xc2\x11\x16\x12\xfb\x8b\xbb\x8e}\xd97\x8dg\xf1\xa3\x84\xf0P\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb2\xa4\x98\xf0;\xd7\x17\x8b\u0627\x89\xa0\x0fR7\xafy\xa3\xe3\xf8\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\xb2\xaa/\x1f\x8e\x93\xe7\x97\x13\xd9,\xea\x9f\xfc\xe9\xa4\n\xf9\xc8-\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xb5\x16\xfd\u045e\u007f8d\xb6\xd2\xcf\x1b%*AV\xf1\xb0;\x89\x02\xe9\x83\xc7a\x15\xfc\x00\x00\u07d4\xb2\xb7\u0374\xffKa\u0577\xce\v\"p\xbb\xb5&\x97C\xec\x04\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb2\xbd\xbe\u07d5\x90\x84v\xd7\x14\x8a7\f\u0193t6(\x05\u007f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb2\xbf\xaaX\xb5\x19l\\\xb7\xf8\x9d\xe1_G\x9d\x188\xdeq=\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xb2\xc5>\xfa3\xfeJ:\x1a\x80 \\s\xec;\x1d\xbc\xad\x06\x02\x89h\x01\u06b3Y\x18\x93\x80\x00\xe0\x94\xb2\xd06\x05\x15\xf1}\xab\xa9\x0f\u02ec\x82\x05\xd5i\xb9\x15\u05ac\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xb2\xd1\xe9\x9a\xf9\x121\x85\x8epe\xdd\x19\x183\r\xc4\xc7G\u054a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xb2\u066b\x96d\xbc\xf6\xdf <4o\u0192\xfd\x9c\xba\xb9 ^\x89\x17\xbex\x97`e\x18\x00\x00\u07d4\xb2\u0777\x86\xd3yN'\x01\x87\xd0E\x1a\xd6\u0237\x9e\x0e\x87E\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb2\xe0\x85\xfd\xdd\x14h\xba\aA['NsN\x11#\u007f\xb2\xa9\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xb2\xe9\xd7k\xf5\x0f\xc3k\xf7\u04d4Kc\xe9\u0288\x9bi\x99h\x89\x902\xeab\xb7K\x10\x00\x00\xe0\x94\xb2\xf9\xc9r\xc1\xe9swU\xb3\xff\x1b0\x88s\x83\x969[&\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xb2\xfc\x84\xa3\xe5\nP\xaf\x02\xf9M\xa08>\u055fq\xff\x01\u05ca\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xb3\x05\v\xef\xf9\xde3\xc8\x0e\x1f\xa1R%\xe2\x8f,A:\xe3\x13\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xb3\x11\x96qJH\xdf\xf7&\xea\x943\xcd)\x12\xf1\xa4\x14\xb3\xb3\x89\x91Hx\xa8\xc0^\xe0\x00\x00\xe0\x94\xb3\x14[tPm\x1a\x8d\x04|\xdc\xdcU9*{SPy\x9a\x8a\x1bb)t\x1c\r=]\x80\x00\u07d4\xb3 \x83H6\xd1\xdb\xfd\xa9\xe7\xa3\x18M\x1a\xd1\xfdC \xcc\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3#\u073f.\xdd\xc58.\u4efb \x1c\xa3\x93\x1b\xe8\xb48\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb3$\x00\xfd\x13\xc5P\t\x17\xcb\x03{)\xfe\"\xe7\xd5\"\x8f-\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xb3%gL\x01\xe3\xf7)\rR&3\x9f\xbe\xacg\xd2!'\x9f\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xb3(%\xd5\xf3\xdb$\x9e\xf4\xe8\\\xc4\xf31S\x95\x89v\u8f09\x1b-\xf9\xd2\x19\xf5y\x80\x00\u07d4\xb3*\xf3\xd3\xe8\xd0u4I&To.2\x88{\xf9;\x16\xbd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3/\x1c&\x89\xa5\xcey\xf1\xbc\x97\v1XO\x1b\xcf\"\x83\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3<\x03#\xfb\xf9\xc2l\x1d\x8a\xc4N\xf7C\x91\u0400F\x96\u0689\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb3O\x04\xb8\xdbe\xbb\xa9\xc2n\xfcL\xe6\xef\xc5\x04\x81\xf3\xd6]\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3U}9\xb5A\x1b\x84D__T\xf3\x8fb\xd2qM\x00\x87\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xb3X\xe9|p\xb6\x05\xb1\xd7\xd7)\u07f6@\xb4<^\xaf\xd1\xe7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xb3^\x8a\x1c\r\xac~\x0ef\u06ecsjY*\xbdD\x01%a\x88\xcf\xceU\xaa\x12\xb3\x00\x00\xe0\x94\xb3fx\x94\xb7\x86<\x06\x8a\xd3D\x87?\xcf\xf4\xb5g\x1e\x06\x89\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb3qw1\xda\xd6Q2\xday-\x87`0\xe4j\xc2'\xbb\x8a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3s\x1b\x04l\x8a\u0195\xa1'\xfdy\u0425\xd5\xfaj\xe6\xd1.\x89lO\xd1\xee$nx\x00\x00\u07d4\xb3|+\x9fPc{\xec\xe0\u0295\x92\b\xae\xfe\xe6F;\xa7 \x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb3\x88\xb5\xdf\xec\xd2\xc5\u4d56W|d%V\xdb\xfe'xU\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb3\x8cNS{]\xf90\xd6Zt\xd0C\x83\x1dkH[\xbd\xe4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb3\x919Wa\x94\xa0\x86a\x95\x15\x1f3\xf2\x14\n\xd1\u0306\u03ca\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xb3\x9fL\x00\xb2c\f\xab}\xb7)^\xf4=G\xd5\x01\xe1\u007f\u05c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb3\xa6K\x11vrOT\t\xe1AJ5#f\x1b\xae\xe7KJ\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xb3\xa6\xbdA\xf9\xd9\xc3 \x1e\x05\v\x87\x19\x8f\xbd\xa3\x994\"\x10\x89\xc4a\xe1\xdd\x10)\xb5\x80\x00\u07d4\xb3\xa8\xc2\xcb}5\x8eW9\x94\x1d\x94[\xa9\x04Z\x02:\x8b\xbb\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xaeT\xfb\xa0\x9d>\xe1\u05bd\xd1\xe9W\x929\x19\x02L5\xfa\x89\x03\x8d,\xeee\xb2*\x80\x00\u07d4\xb3\xb7\xf4\x93\xb4J,\x8d\x80\xecx\xb1\xcd\xc7Ze+s\xb0l\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb3\xc2(s\x1d\x18m-\xed[_\xbe\x00Lfl\x8eF\x9b\x86\x89\x01\x92t\xb2Y\xf6T\x00\x00\u07d4\xb3\xc2``\x9b\x9d\xf4\t^l]\xff9\x8e\xeb^-\xf4\x99\x85\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xb3\xc6[\x84Z\xbal\xd8\x16\xfb\xaa\xe9\x83\xe0\xe4l\x82\xaa\x86\"\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb3\xc9H\x11\xe7\x17[\x14\x8b(\x1c\x1a\x84[\xfc\x9b\xb6\xfb\xc1\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb3\xe2\x0e\xb4\xde\x18\xbd\x06\x02!h\x98\x94\xbe\u5bb2SQ\xee\x89\x03\xfc\x80\xcc\xe5\x16Y\x80\x00\u07d4\xb3\xe3\xc49\x06\x98\x80\x15f\x00\u0089.D\x8dA6\xc9-\x9b\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xb3\xf8*\x87\xe5\x9a9\xd0\u0480\x8f\aQ\xebr\xc22\x9c\xdc\u014a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb3\xfc\x1dh\x81\xab\xfc\xb8\xbe\xcc\v\xb0!\xb8\xb7;r3\u0751\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xb4\x05\x94\xc4\xf3fN\xf8I\u0326\"{\x8a%\xaai\t%\xee\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb4\x1e\xaf]Q\xa5\xba\x1b\xa3\x9b\xb4\x18\u06f5O\xabu\x0e\xfb\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4$\u058d\x9d\r\x00\xce\xc1\x93\x8c\x85N\x15\xff\xb8\x80\xba\x01p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4%bs\x96+\xf61\xd0\x14U\\\xc1\xda\r\xcc1akI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb40g\xfep\u0675Ys\xbaX\xdcd\xdd\u007f1\x1eUBY\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb46W\xa5\x0e\xec\xbc0w\xe0\x05\xd8\xf8\xd9O7xv\xba\u0509\x01\xec\x1b:\x1f\xf7Z\x00\x00\u07d4\xb4<'\xf7\xa0\xa1\"\bK\x98\xf4\x83\x92%A\u0203l\xee,\x89&\u009eG\u0104L\x00\x00\xe0\x94\xb4A5v\x86\x9c\b\xf9Q*\xd3\x11\xfe\x92Y\x88\xa5-4\x14\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb4F\x05U$q\xa6\xee\xe4\u06abq\xff;\xb4\x13&\xd4s\xe0\x89-~=Q\xbaS\xd0\x00\x00\u07d4\xb4GW\x1d\xac\xbb>\u02f6\xd1\xcf\v\f\x8f88\xe5#$\xe2\x89\x01\xa3\x18f\u007f\xb4\x05\x80\x00\u07d4\xb4G\x83\xc8\xe5{H\a\x93\xcb\u059aE\xd9\f{O\fH\xac\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb4H\x15\xa0\xf2\x8eV\x9d\x0e\x92\x1aJ\u078f\xb2d%&Iz\x89\x03\x027\x9b\xf2\xca.\x00\x00\u07d4\xb4Im\xdb'y\x9a\"$W\xd79y\x11g(\u8844[\x89\x8d\x81\x9e\xa6_\xa6/\x80\x00\xe0\x94\xb4RL\x95\xa7\x86\x0e!\x84\x02\x96\xa6\x16$@\x19B\x1cJ\xba\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb4\\\xca\r6\x82fbh<\xf7\u0432\xfd\xach\u007f\x02\xd0\u010965\u026d\xc5\u07a0\x00\x00\u0794\xb4d@\u01d7\xa5V\xe0L}\x91\x04f\x04\x91\xf9k\xb0v\xbf\x88\xce\xc7o\x0eqR\x00\x00\u07d4\xb4j\u0386^,P\xeaF\x98\xd2\x16\xabE]\xffZ\x11\xcdr\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4m\x11\x82\xe5\xaa\xca\xff\r&\xb2\xfc\xf7/<\x9f\xfb\xcd\xd9}\x89\xaa*`<\xdd\u007f,\x00\x00\u07d4\xb4\x89!\xc9h}U\x10tE\x84\x93n\x88\x86\xbd\xbf-\xf6\x9b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\x98\xbb\x0fR\x00\x05\xb6!jD%\xb7Z\xa9\xad\xc5-b+\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb4\xb1\x1d\x10\x9f`\x8f\xa8\xed\xd3\xfe\xa9\xf8\xc3\x15d\x9a\xeb=\x11\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb4\xb1K\xf4TU\u042b\b\x035\x8bu$\xa7+\xe1\xa2\x04[\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb4\xb1\x85\xd9C\xee+Xc\x1e3\xdf\xf5\xafhT\xc1y\x93\xac\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xbf$\u02c3hk\xc4i\x86\x9f\xef\xb0D\xb9\tqi\x93\xe2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb4\xc2\x00@\xcc\u0661\xa3(=\xa4\u0522\xf3e\x82\bC\xd7\xe2\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb4\xc8\x17\x0f{*\xb56\xd1\u0662[\xdd :\xe1(\x8d\xc3\u0549\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb4\xd8/.i\x94?}\xe0\xf5\xf7t8y@o\xac.\x9c\xec\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xb4\xddF\f\xd0\x16rZd\xb2.\xa4\xf8\xe0n\x06gN\x03>\x8a\x01#\x1b\xb8t\x85G\xa8\x00\x00\u07d4\xb4\xddT\x99\xda\xeb%\a\xfb-\xe1\"\x97s\x1dLr\xb1k\xb0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb5\x04l\xb3\xdc\x1d\xed\xbd6E\x14\xa2\x84\x8eD\xc1\xdeN\xd1G\x8a\x03{}\x9b\xb8 @^\x00\x00\xe0\x94\xb5\b\xf9\x87\xb2\xde4\xaeL\xf1\x93\u0785\xbf\xf6\x13\x89b\x1f\x88\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb5\tU\xaan4\x15q\x98f\b\xbd\u0211\xc2\x13\x9fT\f\u07c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5\f\x14\x9a\x19\x06\xfa\xd2xo\xfb\x13Z\xabP\x177\xe9\xe5o\x89\x15\b\x94\xe8I\xb3\x90\x00\x00\u07d4\xb5\f\x9fW\x89\xaeD\xe2\xdc\xe0\x17\xc7\x14\xca\xf0\f\x83\x00\x84\u0089\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xb5\x14\x88,\x97\x9b\xb6B\xa8\r\u04c7T\u0578\xc8)m\x9a\a\x893\xc5I\x901r\f\x00\x00\u07d4\xb5\x1d\u0734\xddN\x8a\xe6\xbe3m\xd9eIq\xd9\xfe\xc8kA\x89\x16\xd4d\xf8=\u2500\x00\u07d4\xb5\x1eU\x8e\xb5Q/\xbc\xfa\x81\xf8\u043d\x93\x8cy\xeb\xb5$+\x89&\u009eG\u0104L\x00\x00\u07d4\xb5#\xff\xf9t\x98q\xb3S\x88C\x887\xf7\xe6\xe0\u07a9\xcbk\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb5-\xfbE\xde]t\xe3\xdf \x832\xbcW\x1c\x80\x9b\x8d\xcf2\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xb55\xf8\u06c7\x9f\xc6\u007f\xecX\x82J\\\xbenT\x98\xab\xa6\x92\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xb57\xd3jp\xee\xb8\xd3\xe5\xc8\r\xe8\x15\"\\\x11X\u02d2\u0109QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xb5;\xcb\x17L%\x184\x8b\x81\x8a\xec\xe0 6E\x96Fk\xa3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5I>\xf1srDE\xcf4\\\x03]'\x9b\xa7Y\xf2\x8dQ\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb5S\xd2]kT!\xe8\x1c*\xd0^\v\x8b\xa7Q\xf8\xf0\x10\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5Tt\xbaX\xf0\xf2\xf4\x0el\xba\xbe\xd4\xea\x17n\x01\x1f\xca\u0589j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb5U\xd0\x0f\x91\x90\xcc6w\xae\xf3\x14\xac\xd7?\xdc99\x92Y\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5W\xab\x949\xefP\xd27\xb5S\xf0%\b6JFj\\\x03\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5jx\x00(\x03\x9c\x81\xca\xf3{gu\xc6 \u7195Gd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5j\u04ae\xc6\xc8\xc3\xf1\x9e\x15\x15\xbb\xb7\u0751(RV\xb69\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5t\x13\x06\n\xf3\xf1N\xb4y\x06_\x1e\x9d\x19\xb3uz\xe8\u0309\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xb5uI\xbf\xbc\x9b\xdd\x18\xf76\xb2&P\xe4\x8as`\x1f\xa6\\\x89\x18-~L\xfd\xa08\x00\x00\xe0\x94\xb5w\xb6\xbe\xfa\x05N\x9c\x04\x04a\x85P\x94\xb0\x02\xd7\xf5{\u05ca\x18#\xf3\xcfb\x1d#@\x00\x00\u07d4\xb5{\x04\xfa#\xd1 ?\xae\x06\x1e\xacEB\xcb`\xf3\xa5v7\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xb5\x87\f\xe3B\xd43C36s\x03\x8bGd\xa4n\x92_>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\x87\xb4J,\xa7\x9eK\xc1\u074b\xfd\xd4: qP\xf2\xe7\xe0\x89\",\x8e\xb3\xfff@\x00\x00\u07d4\xb5\x89gm\x15\xa0DH4B0\xd4\xff'\xc9^\xdf\x12,I\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb5\x8bR\x86^\xa5]\x806\xf2\xfa\xb2`\x98\xb3R\u0283~\x18\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xb5\x90k\n\u9881X\xe8\xacU\x0e9\xda\bn\xe3\x15v#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xa4g\x96\x85\xfa\x14\x19l.\x920\xc8\xc4\xe3;\xff\xbc\x10\xe2\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xb5\xa5\x89\u075f@q\u06f6\xfb\xa8\x9b?]]\xae}\x96\xc1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xa6\x06\xf4\xdd\u02f9G\x1e\xc6\u007fe\x8c\xaf+\x00\xees\x02^\x89\xeaun\xa9*\xfct\x00\x00\u07d4\xb5\xadQW\u0769!\xe6\xba\xfa\u0350\x86\xaes\xae\x1fa\x1d?\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb5\xad\xd1\u701f}\x03\x06\x9b\xfe\x88;\n\x93\"\x10\xbe\x87\x12\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb5\xba)\x91|x\xa1\xd9\xe5\xc5\xc7\x13fl\x1eA\x1d\u007fi:\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb5\xc8\x16\xa8(<\xa4\xdfh\xa1\xa7=c\xbd\x80&\x04\x88\xdf\b\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb5\xca\xc5\xed\x03G}9\v\xb2g\xd4\xeb\xd4a\x01\xfb\xc2\xc3\u0689\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xb5\u037cA\x15@oR\u5a85\xd0\xfe\xa1p\u0497\x9c\u01fa\x89Hz\x9a0E9D\x00\x00\u0794\xb5\u0653M{)+\xcf`;(\x80t\x1e\xb7`(\x83\x83\xa0\x88\xe7\xc2Q\x85\x05\x06\x00\x00\u07d4\xb5\xddP\xa1]\xa3Ih\x89\nS\xb4\xf1?\xe1\xaf\b\x1b\xaa\xaa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb5\xfa\x81\x84\xe4>\xd3\u0e2b\x91!da\xb3R\x8d\x84\xfd\t\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb5\xfb~\xa2\xdd\xc1Y\x8bfz\x9dW\xdd9\xe8Z8\xf3]V\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb6\x00B\x97R\xf3\x99\xc8\r\a4tK\xae\n\x02.\xcag\u0189\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x00\xfe\xabJ\xa9lSu\x04\xd9`W\"1Ai,\x19:\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xb6\x04|\u07d3-\xb3\xe4\x04_Iv\x12#AS~\u0556\x1e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb6\x15\xe9@\x14>\xb5\u007f\x87X\x93\xbc\x98\xa6\x1b=a\x8c\x1e\x8c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xb6\x1c4\xfc\xac\xdap\x1aZ\xa8p$Y\u07b0\u4b83\x8d\xf8\x8a\aiZ\x92\xc2\ro\xe0\x00\x00\xe0\x94\xb60d\xbd3U\xe6\xe0~-7p$\x12Z3wlJ\xfa\x8a\b7Z*\xbc\xca$@\x00\x00\u07d4\xb65\xa4\xbcq\xfb(\xfd\xd5\xd2\xc3\"\x98:V\u0084Bni\x89\t79SM(h\x00\x00\u07d4\xb6F\u07d8\xb4\x94BtkaR\\\x81\xa3\xb0K\xa3\x10bP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xb6YA\xd4LP\xd2Ffg\r6Gf\xe9\x91\xc0.\x11\u0089 \x86\xac5\x10R`\x00\x00\xe0\x94\xb6[\u05c0\xc7CA\x15\x16 'VR#\xf4NT\x98\xff\x8c\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xb6d\x11\xe3\xa0-\xed\xb7&\xfay\x10}\xc9\v\xc1\xca\xe6MH\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb6fu\x14.1\x11\xa1\xc2\xea\x1e\xb2A\x9c\xfaB\xaa\xf7\xa24\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xb6o\x92\x12K^c\x03XY\xe3\x90b\x88i\xdb\u07a9H^\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xb6rsJ\xfc\xc2$\xe2\xe6\t\xfcQ\xd4\xf0Ys'D\xc9H\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\xe0\x94\xb6w\x1b\v\xf3B\u007f\x9a\xe7\xa9>|.a\xeec\x94\x1f\xdb\b\x8a\x03\xfb&i)T\xbf\xc0\x00\x00\u07d4\xb6z\x80\xf1p\x19}\x96\xcd\xccJ\xb6\u02e6'\xb4\xaf\xa6\xe1,\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xb6\x88\x99\xe7a\rL\x93\xa255\xbc\xc4H\x94[\xa1fo\x1c\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb6\xa8)3\xc9\xea\u06bd\x98\x1e]m`\xa6\x81\x8f\xf8\x06\xe3k\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xb6\xaa\u02cc\xb3\v\xab*\xe4\xa2BF&\xe6\xe1+\x02\xd0F\x05\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb6\xb3J&?\x10\xc3\xd2\xec\xeb\n\xccU\x9a{*\xb8\\\xe5e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xbf\xe1\xc3\xef\x94\xe1\x84o\xb9\xe3\xac\xfe\x9bP\xc3\xe9\x06\x923\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xb6\xcdt2\xd5\x16\x1b\xe7\x97h\xadE\xde>Dz\a\x98 c\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb6\xceM\xc5`\xfcs\xdci\xfbzb\xe3\x88\xdb~r\xeavO\x894]\xf1i\xe9\xa3X\x00\x00\u07d4\xb6\xde\u03c2\x96\x98\x19\xba\x02\xde)\xb9\xb5\x93\xf2\x1bd\xee\xda\x0f\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\xe0\x94\xb6\xe6\xc3\"+ko\x9b\xe2\x87]*\x89\xf1'\xfbd\x10\x0f\xe2\x8a\x01\xb2\x1dS#\xcc0 \x00\x00\u07d4\xb6\xe8\xaf\xd9=\xfa\x9a\xf2\u007f9\xb4\xdf\x06\ag\x10\xbe\xe3\u07eb\x89\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xb6\xf7\x8d\xa4\xf4\xd0A\xb3\xbc\x14\xbc[\xa5\x19\xa5\xba\f2\xf1(\x8a$}\xd3,?\xe1\x95\x04\x80\x00\xe0\x94\xb6\xfb9xbP\b\x14&\xa3B\xc7\rG\xeeR\x1e[\xc5c\x8a\x03-&\xd1.\x98\v`\x00\x00\u07d4\xb7\r\xba\x93\x91h+J6Nw\xfe\x99%c\x01\xa6\xc0\xbf\x1f\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x16#\xf3Q\a\xcft1\xa8?\xb3\xd2\x04\xb2\x9e\u0c67\xf4\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xb7\x1a\x13\xba\x8e\x95\x16{\x803\x1bR\u059e7\x05O\xe7\xa8&\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\x1bb\xf4\xb4H\xc0+\x12\x01\xcb^9J\xe6'\xb0\xa5`\xee\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb7\" \xad\xe3d\xd06\x9f--\xa7\x83\xcaGM{\x9b4\u0389\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\xe0\x94\xb7#\r\x1d\x1f\xf2\xac\xa3f\x969\x14\xa7\x9d\xf9\xf7\xc5\xea,\x98\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xb7$\n\U000af433<\b\xae\x97d\x10>5\xdc\xe3c\x84(\x8a\x01\xca\xdd/\xe9hnc\x80\x00\u07d4\xb7'\xa9\xfc\x82\xe1\xcf\xfc\\\x17_\xa1HZ\x9b\xef\xa2\u037d\u04496'\xe8\xf7\x127<\x00\x00\u07d4\xb7,*\x01\x1c\r\xf5\x0f\xbbn(\xb2\n\xe1\xaa\xd2\x17\x88g\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb78-7\xdb\x03\x98\xacrA\f\xf9\x81=\xe9\xf8\xe1\uc36d\x8966\xc2^f\xec\xe7\x00\x00\u07d4\xb7;O\xf9\x9e\xb8\x8f\u061b\vmW\xa9\xbc3\x8e\x88o\xa0j\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xb7=jwU\x9c\x86\xcfet$)\x039K\xac\xf9n5p\x89\x04\xf1\xa7|\xcd;\xa0\x00\x00\u07d4\xb7Cr\xdb\xfa\x18\x1d\xc9$/9\xbf\x1d71\xdf\xfe+\xda\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7G\x9d\xabP\"\xc4\xd5\u06ea\xf8\xde\x17\x1bN\x95\x1d\u0464W\x89\x04V9\x18$O@\x00\x00\u07d4\xb7I\xb5N\x04\u0571\x9b\xdc\xed\xfb\x84\xdaw\x01\xabG\x8c'\xae\x89\x91Hx\xa8\xc0^\xe0\x00\x00\u07d4\xb7N\xd2f`\x01\xc1c3\xcfz\xf5\x9eJ=H`6;\x9c\x89\n~\xbd^Cc\xa0\x00\x00\u07d4\xb7QI\xe1\x85\xf6\xe3\x92pWs\x90s\xa1\x82*\xe1\xcf\r\xf2\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xb7S\xa7_\x9e\xd1\v!d:\n=\xc0Qz\xc9k\x1a@h\x89\x15\xc8\x18[,\x1f\xf4\x00\x00\xe0\x94\xb7V\xadR\xf3\xbft\xa7\xd2LgG\x1e\b\x87Ci6PL\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb7Wn\x9d1M\xf4\x1e\xc5Pd\x94):\xfb\x1b\xd5\xd3\xf6]\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb7X\x89o\x1b\xaa\x86O\x17\xeb\xed\x16\xd9S\x88o\xeeh\xaa\xe6\x8965\u026d\xc5\u07a0\x00\x00\u0794\xb7h\xb5#N\xba:\x99h\xb3Mm\xdbH\x1c\x84\x19\xb3e]\x88\xcf\xceU\xaa\x12\xb3\x00\x00\u07d4\xb7\x82\xbf\xd1\xe2\xdep\xf4gdo\x9b\xc0\x9e\xa5\xb1\xfc\xf4P\xaf\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xb7\xa2\xc1\x03r\x8bs\x05\xb5\xaen\x96\x1c\x94\xee\x99\xc9\xfe\x8e+\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xb7\xa3\x1a|8\xf3\xdb\t2.\xae\x11\xd2'!A\xea\"\x99\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xa6y\x1c\x16\xebN!b\xf1Ke7\xa0+=c\xbf\xc6\x02\x89*Rc\x91\xac\x93v\x00\x00\u07d4\xb7\xa7\xf7|4\x8f\x92\xa9\xf1\x10\fk\xd8)\xa8\xacm\u007f\u03d1\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xb7\xc0w\x94ft\xba\x93A\xfbLtz]P\xf5\xd2\xdad\x15\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xb7\xc0\xd0\xcc\vM4-@b\xba\xc6$\xcc\xc3\xc7\f\xc6\xda?\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb7\xc9\xf1+\x03\x8esCm\x17\xe1\xc1/\xfe\x1a\xec\u0373\xf5\x8c\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xb7\xcck\x1a\xcc2\u0632\x95\xdfh\xed\x9d^`\xb8\xf6L\xb6{\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xb7\xcehK\t\xab\xdaS8\x9a\x87Si\xf7\x19X\xae\xac;\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xb7\xd1.\x84\xa2\xe4\u01264Z\xf1\xdd\x1d\xa9\xf2PJ*\x99n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb7\xd2R\xee\x94\x02\xb0\xee\xf1D)_\x0ei\xf0\xdbXl\bq\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\xb7\u0541\xfe\n\xf1\xec8?;<Ag\x83\xf3\x85\x14jv\x12\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xb7\xf6s\x14\u02c3.2\xe6;\x15\xa4\f\xe0\xd7\xff\xbd\xb2i\x85\x899\x82y&J\x81\x8d\x00\x00\xe0\x94\xb8\x04\x056\x95\x8dY\x98\xceK\xec\f\xfc\x9c\"\x04\x98\x98H\xe9\x8a\x05.\xa7\rI\x8f\xd5\n\x00\x00\u07d4\xb81\n\x16\xccj\xbcFP\aiK\x93\x0f\x97\x8e\xce\x190\xbd\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\xe0\x94\xb84\xac\xf3\x01S\"\u0143\x82\uecb7\x968\x90n\x88\xb6\u078a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\xe0\x94\xb8KS\u043b\x12VV\xcd\xdcR\xeb\x85*\xb7\x1drY\xf3\u054a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xb8L\x8b\x9f\xd3>\xce\x00\xaf\x91\x99\xf3\xcf_\xe0\xcc\xe2\x8c\xd1J\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xb8R\x18\xf3B\xf8\x01.\u069f'Nc\xce!R\xb2\xdc\xfd\xab\x89\xa8\r$g~\xfe\xf0\x00\x00\u07d4\xb8UP\x10wn<\\\xb3\x11\xa5\xad\xee\xfe\x9e\x92\xbb\x9ad\xb9\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xb8_&\xdd\x0er\xd9\u009e\xba\xf6\x97\xa8\xafwG,+X\xb5\x8a\x02\x85\x19\xac\xc7\x19\fp\x00\x00\u07d4\xb8_\xf0>{_\xc4\"\x98\x1f\xae^\x99A\xda\xcb\u06bau\x84\x89Hz\x9a0E9D\x00\x00\xe0\x94\xb8f\a\x02\x1bb\xd3@\xcf&R\xf3\xf9_\xd2\xdcgi\x8b\u07ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xb8}\xe1\xbc\u0492i\xd5!\xb8v\x1c\u00dc\xfbC\x19\xd2\xea\u054965\u026d\xc5\u07a0\x00\x00\u07d4\xb8\u007fSv\xc2\xde\vl\xc3\xc1y\xc0`\x87\xaaG=kFt\x89Hz\x9a0E9D\x00\x00\u07d4\xb8\x84\xad\u060d\x83\xdcVJ\xb8\xe0\xe0,\xbd\xb69\x19\xae\xa8D\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x8a7\xc2\u007fx\xa6\x17\xd5\xc0\x91\xb7\u0577:7a\xe6_*\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\x94x\"\u056c\u79ad\x83&\xe9T\x96\"\x1e\v\xe6\xb7=\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\x9c\x03n\xd7\u0112\x87\x99!\xbeA\xe1\f\xa1i\x81\x98\xa7L\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xb8\x9fF2\xdfY\t\xe5\x8b*\x99d\xf7O\xeb\x9a;\x01\xe0\u014a\x04\x88u\xbc\xc6\xe7\xcb\xeb\x80\x00\u07d4\xb8\xa7\x9c\x84\x94^G\xa9\xc3C\x86\x83\u05b5\x84,\xffv\x84\xb1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb8\xa9y5'Y\xba\t\xe3Z\xa5\x93]\xf1u\xbf\xf6x\xa1\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xb8\xab9\x80[\xd8!\x18Ol\xbd=$s4{\x12\xbf\x17\\\x89\x06hZ\xc1\xbf\xe3,\x00\x00\xe0\x94\xb8\xac\x11}\x9f\r\xba\x80\x90\x14E\x82<L\x9dO\xa3\xfe\xdcn\x8a\x03VLD'\xa8\xfc}\x80\x00\u07d4\xb8\xbc\x9b\xca\u007fq\xb4\xed\x12\xe6 C\x8db\x0fS\xc1\x144/\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xb8\xbe\xddWjKL '\xdasZ[\xc3\xf53%*\x18\b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb8\xc2p=\x8c?/D\u0144\xbc\x10\xe7\xc0\xa6\xb6L\x1c\t~\x8a\x01,\u0778\xea\xd6\xf9\xf8\x00\x00\xe0\x94\xb8\xcc\x0f\x06\n\xad\x92\xd4\xeb\x8b6\xb3\xb9\\\xe9\xe9\x0e\xb3\x83\u05ca\x1f\u00c4+\xd1\xf0q\xc0\x00\x00\u07d4\xb8\xd2\xdd\xc6o0\x8c\x01X\xae<\xcb{\x86\x9f}\x19\x9d{2\x89-\xcb\xf4\x84\x0e\xca\x00\x00\x00\u07d4\xb8\u04c9\xe6$\xa3\xa7\xae\xbc\xe4\xd3\xe5\xdb\xdfl\xdc)\x93*\xed\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xb8\xd51\xa9d\xbc\xea\x13\x82\x96 \xc0\xce\xd7$\"\xda\xdbL\u0289\t7\x15\xccZ\xb8\xa7\x00\x00\xe0\x94\xb8\xd5\xc3$\xa8 \x9d|\x80I\xd0\u052e\xde\x02\xba\x80\xabW\x8b\x8a\x03\x93\x92\x86)\xff\xf7^\x80\x00\xe0\x94\xb8\xf2\x00\x05\xb6\x13R\xff\xa7i\x9a\x1bR\xf0\x1fZ\xb3\x91g\xf1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xb8\xf3\aX\xfa\xa8\b\xdb\xc9\x19\xaa{B^\xc9\"\xb9;\x81)\x8966\u05ef^\u024e\x00\x00\u07d4\xb9\x01<Q\xbd\a\x8a\t\x8f\xae\x05\xbf*\xce\bI\u01be\x17\xa5\x89\x04V9\x18$O@\x00\x00\u07d4\xb9\x14Kg|-\xc6\x14\xce\xef\xdfP\x98_\x11\x83 \x8e\xa6L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb9\x16\xb1\xa0\x1c\xdcNV\xe7ew\x15\xea7\xe2\xa0\xf0\x87\xd1\x06\x89\x82n1\x81\xe0'\x06\x80\x00\u07d4\xb9\x1d\x9e\x91l\xd4\r\x19=\xb6\x0ey 'x\xa0\bw\x16\xfc\x89\x15\xf1\xba\u007fG\x16 \x00\x00\xe0\x94\xb9#\x1e\xb2n_\x9eKM(\x8f\x03\x90g\x04\xfa\xb9l\x87\u058a\x04+\xf0kx\xed;P\x00\x00\u07d4\xb9$'\xadux\xb4\xbf\xe2\n\x9fc\xa7\xc5Pm\\\xa1-\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\xb9'\xab\xd2\u048a\xaa\xa2M\xb3\x17x\xd2t\x19\u07ce\x1b\x04\xbb\x89\x01~\x11\u00a2oG\x80\x00\u07d4\xb9MG\xb3\xc0R\xa5\xe5\x0eBa\xae\x06\xa2\x0fE\xd8\xee\u25c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xb9S\x96\u06aaI\r\xf2V\x93$\xfc\xc6b;\xe0R\xf12\u0289lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xb9Y\xdc\xe0.\x91\xd9\xdb\x02\xb1\xbd\x8b}\x17\xa9\xc4\x1a\x97\xaf\t\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xb9\\\x9b\x10\xaa\x98\x1c\xf4\xa6zq\xccR\xc5\x04\xde\xe8\xcfX\xbd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb9\\\xfd\xa8F[\xa9\xc2f\x1b$\x9f\u00ebf\x1b\u07e3_\xf0\x89\x11JNy\xa2\xc2\x10\x80\x00\xe0\x94\xb9hA\u02bb\xc7\xdb\u059e\xf0\u03cf\x81\xdf\xf3\u0225\xe2\x15p\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xb9zg3\xcd_\xe9\x98d\xb3\xb34`\xd1g$4\xd5\xca\xfd\x89le\xbb\xaaF\xc2\u03c0\x00\xe0\x94\xb9\x81\xad^kw\x93\xa2?\xc6\xc1\xe8i.\xb2\x96]\x18\xd0\u068a\x02\x1e\x18\xd2\xc8!\xc7R\x00\x00\u07d4\xb9\x8c\xa3\x17\x85\xef\x06\xbeI\xa1\xe4~\x86O`\xd0v\xcaG.\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xb9\x92\x0f\xd0\xe2\xc75\xc2VF<\xaa$\x0f\xb7\xac\x86\xa9=\xfa\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xb9\x92\xa9g0\x8c\x02\xb9\x8a\xf9\x1e\xe7`\xfd;kH$\xab\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xb9\xa9\x85P\x1e\xe9P\x82\x9b\x17\xfa\xe1\xc9\xcf4\x8c1VT,\x89\x0f\xf1u\x17\u029ab\x00\x00\xe0\x94\xb9\xb0\xa3!\x9a2\x88\u0673[\t\x1b\x14e\v\x8f\xe2=\xce+\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xb9\xcfq\xb2&X>:\x92\x11\x03\xa51o\x85Zew\x9d\x1b\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xb9\xe9\f\x11\x92\xb3\xd5\xd3\xe3\xab\a\x00\xf1\xbfe_]\xd44z\x89\x1b\x19\xe5\vD\x97|\x00\x00\u07d4\xb9\xfd83\xe8\x8e|\xf1\xfa\x98y\xbd\xf5Z\xf4\xb9\x9c\xd5\xce?\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xba\x02I\xe0\x1d\x94[\xef\x93\xee^\xc6\x19%\xe0<\\\xa5\t\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\x0f9\x02;\xdb)\xeb\x18b\xa9\xf9\x05\x9c\xab]0nf/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x10\xf2vB\x90\xf8uCCr\xf7\x9d\xbfq8\x01\u02ac\x01\x893\xc5I\x901r\f\x00\x00\u07d4\xba\x151\xfb\x9ey\x18\x96\xbc\xf3\xa8\x05X\xa3Y\xf6\xe7\xc1D\xbd\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xba\x17m\xbe2I\xe3E\xcdO\xa9g\xc0\xed\x13\xb2LG\u5189\x15\xae\xf9\xf1\xc3\x1c\u007f\x00\x00\xe0\x94\xba\x1f\x0e\x03\u02da\xa0!\xf4\xdc\xeb\xfa\x94\xe5\u0209\xc9\u01fc\x9e\x8a\x06\u0450\xc4u\x16\x9a \x00\x00\u07d4\xba\x1f\xca\xf2#\x93~\xf8\x9e\x85gU\x03\xbd\xb7\xcaj\x92\x8bx\x89\"\xb1\xc8\xc1\"z\x00\x00\x00\xe0\x94\xba$\xfcCgS\xa79\xdb,\x8d@\xe6\xd4\xd0LR\x8e\x86\xfa\x8a\x02\xc0\xbb=\xd3\fN \x00\x00\u07d4\xbaB\xf9\xaa\xceL\x18E\x04\xab\xf5BWb\xac\xa2oq\xfb\u0709\x02\a\a}\u0627\x9c\x00\x00\u07d4\xbaF\x9a\xa5\u00c6\xb1\x92\x95\u0521\xb5G;T\x03S9\f\x85\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbad@\xae\xb3s{\x8e\xf0\xf1\xaf\x9b\f\x15\xf4\xc2\x14\xff\xc7\u03c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbam1\xb9\xa2a\xd6@\xb5\u07a5\x1e\xf2\x16,1\t\xf1\uba0a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xbap\xe8\xb4u\x9c\f<\x82\xcc\x00\xacN\x9a\x94\xdd[\xaf\xb2\xb8\x890C\xfa3\xc4\x12\xd7\x00\x00\u07d4\xba\x8ac\xf3\xf4\r\u4a03\x88\xbcP!/\xea\x8e\x06O\xbb\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\x8eF\u059d.#C\xd8l`\xd8,\xf4, A\xa0\xc1\u0089\x05k\xc7^-c\x10\x00\x00\u07d4\xba\xa4\xb6L+\x15\xb7\x9f_ BF\xfdp\xbc\xbd\x86\xe4\xa9*\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xba\u0212,J\xcc},\xb6\xfdY\xa1N\xb4\\\xf3\xe7\x02!K\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xba\xd25\xd5\b]\u01f0h\xa6|A&w\xb0>\x186\x88L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xba\xd4B^\x17\x1c>r\x97^\xb4j\xc0\xa0\x15\xdb1Z]\x8f\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xba\xdc*\xef\x9fYQ\xa8\u05cak5\xc3\u0433\xa4\xe6\xe2\xe79\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xba\xdeCY\x9e\x02\xf8OL0\x14W\x1c\x97k\x13\xa3le\xab\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xba\xe9\xb8/r\x99c\x14\be\x9d\xd7N\x89\x1c\xb8\xf3\x86\x0f\xe5\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xbb\x03f\xa7\u03fd4E\xa7\r\xb7\xfeZ\xe3H\x85uO\xd4h\x8a\x01M\xef,B\xeb\xd6@\x00\x00\u07d4\xbb\aj\xac\x92 \x80i\xea1\x8a1\xff\x8e\xeb\x14\xb7\xe9\x96\xe3\x89\b\x13\xcaV\x90m4\x00\x00\u07d4\xbb\bW\xf1\xc9\x11\xb2K\x86\u0227\x06\x81G?\u6aa1\xcc\xe2\x89\x05k\xc7^-c\x10\x00\x00\u0794\xbb\x19\xbf\x91\u02edt\xcc\xeb_\x81\x1d\xb2~A\x1b\xc2\xea\x06V\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xbb'\u01a7\xf9\x10uGZ\xb2)a\x90@\xf8\x04\xc8\xeczj\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xbb7\x1cr\xc9\xf01l\xea+\xd9\xc6\xfb\xb4\a\x9ewT)\xef\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94\xbb;\x01\v\x18\xe6\xe2\xbe\x115\x87\x10&\xb7\xba\x15\xea\x0f\xde$\x8a\x02 |\x800\x9bwp\x00\x00\xe0\x94\xbb;\x90\x05\xf4o\xd2\xca;0\x16%\x99\x92\x8cw\xd9\xf6\xb6\x01\x8a\x01\xb1\xae\u007f+\x1b\xf7\xdb\x00\x00\u07d4\xbb?\xc0\xa2\x9c\x03Mq\b\x12\xdc\xc7u\xc8\u02b9\u048diu\x899\xd4\xe8D\xd1\xcf_\x00\x00\u07d4\xbbH\xea\xf5\x16\xce-\xec>A\xfe\xb4\xc6y\xe4\x95vA\x16O\x89\xcf\x15&@\xc5\xc80\x00\x00\u07d4\xbbKJKT\x80p\xffAC,\x9e\b\xa0\xcao\xa7\xbc\x9fv\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\u07d4\xbbV\xa4\x04r<\xff \xd0hT\x88\xb0Z\x02\xcd\xc3Z\xac\xaa\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbba\x8e%\"\x1a\u0667@\xb2\x99\xed\x14\x06\xbc94\xb0\xb1m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbba\xa0K\xff\xd5|\x10G\rE\u00d1\x03\xf6FP4v\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbbh#\xa1\xbd\x81\x9f\x13QU8&J-\xe0R\xb4D\"\b\x89\x01ch\xffO\xf9\xc1\x00\x00\u07d4\xbbl(J\xac\x8ai\xb7\\\u0770\x0f(\xe1EX;V\xbe\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xbbu\xcbPQ\xa0\xb0\x94KFs\xcau*\x97\x03\u007f|\x8c\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbb\x99;\x96\xee\x92Z\xda}\x99\u05c6W=?\x89\x18\f\u3a89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa3\u0180\x04$\x8eH\x95s\xab\xb2t6w\x06k$\u0227\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xa4\xfa\xc3\xc4 9\xd8(\xe7B\xcd\xe0\xef\xff\xe7t\x94\x1b9\x89lj\u04c2\xd4\xfba\x00\x00\u07d4\xbb\xa8\xab\"\xd2\xfe\xdb\xcf\xc6?hL\b\xaf\xdf\x1c\x17P\x90\xb5\x89\x05_)\xf3~N;\x80\x00\u07d4\xbb\xa9v\xf1\xa1!_u\x12\x87\x18\x92\xd4_pH\xac\xd3V\u0209lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbb\xab\x00\v\x04\b\xed\x01Z7\xc0GG\xbcF\x1a\xb1N\x15\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xbb\xab\xf6d;\xebK\xd0\x1c\x12\v\xd0Y\x8a\t\x87\xd8)g\u0449\xb52\x81x\xad\x0f*\x00\x00\u07d4\xbb\xb4\xee\x1d\x82\xf2\xe1VD,\xc938\xa2\xfc(o\xa2\x88d\x89JD\x91\xbdm\xcd(\x00\x00\u07d4\xbb\xb5\xa0\xf4\x80,\x86H\x00\x9e\x8ai\x98\xaf5,\u0787TO\x89\x05-T(\x04\xf1\xce\x00\x00\u07d4\xbb\xb6C\xd2\x18{6J\xfc\x10\xa6\xfd6\x8d}U\xf5\r\x1a<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbb\xb8\xff\xe4?\x98\u078e\xae\x18F#\xaeRd\xe4$\u0438\u05c9\x05\xd5?\xfd\xe9(\b\x00\x00\u07d4\xbb\xbdn\u02f5u(\x91\xb4\u03b3\xcc\xe7:\x8fGpY7o\x89\x01\xf3\x99\xb1C\x8a\x10\x00\x00\u07d4\xbb\xbf9\xb1\xb6y\x95\xa4\"APO\x97\x03\u04a1JQV\x96\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbb\xc8\xea\xffc~\x94\xfc\u014d\x91<wp\u020f\x9bG\x92w\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbb\xc9\xd8\x11.[\xeb\x02\xdd)\xa2%{\x1f\xe6\x9b56\xa9E\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xcae\xb3&n\xa2\xfbs\xa0?\x92\x165\xf9\x12\u01fe\xde\x00\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbb\xf8B\x92\xd9T\xac\xd9\xe4\a/\xb8`\xb1PA\x06\xe0w\xae\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xbb\xf8Z\xaa\xa6\x83s\x8f\a;\xae\xf4J\xc9\xdc4\xc4\xc7y\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbb\xf8am\x97rJ\xf3\xde\xf1e\xd0\xe2\x8c\u0689\xb8\x00\x00\x9a\x89\x06.\xf1.+\x17a\x80\x00\xe0\x94\xbb\xfe\n\x83\f\xac\xe8{r\x93\x99:~\x94\x96\xced\xf8\u350a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xbc\f\xa4\xf2\x17\xe0Ru6\x14\u05b0\x19\x94\x88$\xd0\xd8h\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xbc\x0e\x87E\u00e5ID\\+\xe9\x00\xf5#\x00\x80J\xb5b\x89\x8a\a\x02\x9b\xf5\xddLS\xb2\x80\x00\u07d4\xbc\x0f\x98Y\x8f\x88\x05j&3\x96 \x92;\x8f\x1e\xb0t\xa9\xfd\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\x16\t\u0585\xb7kH\uc41a\xa0\x99!\x90\"\xf8\x9b,\u0349@\x13\x8b\x91~\u07f8\x00\x00\xe0\x94\xbc\x17\x1eS\xd1z\u0276\x12A\xaeCm\xee\u01efE.t\x96\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xbc\x1b\x02\x1ax\xfd\xe4-\x9bR&\xd6\xec&\xe0j\xa3g\x00\x90\x89\x04V9\x18$O@\x00\x00\u07d4\xbc\x1e\x80\xc1\x81acB\xeb\xb3\xfb9\x92\a/\x1b(\xb8\x02\u0189\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbc#qH\xd3\f\x13\x83o\xfa,\xadR\x0e\xe4\xd2\xe5\xc4\xee\xff\x89j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xbcF\xd57\xcf.\xdd@5e\xbd\xe73\xb2\xe3K!P\x01\xbd\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xbcNG\x15`\u025c\x8a*K\x1b\x1a\xd0\xc3j\xa6P+|K\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xbcb\xb3\tj\x91\xe7\xdc\x11\xa1Y*)=\xd2T!P\xd7Q\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbci\xa0\u04a3\x1c=\xbfz\x91\"\x11i\x01\xb2\xbd\xfe\x98\x02\xa0\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xbckX6K\xf7\xf1\x95\x1c0\x9e\f\xba\x05\x95 \x1c\xd7?\x9a\x89b@\x1aE~E\xf8\x00\x00\u07d4\xbcs\xf7\xb1\xca;w;4$\x9a\xda.,\x8a\x92t\xcc\x17\u0089lk\x93[\x8b\xbd@\x00\x00\u07d4\xbcz\xfc\x84wA\"t\xfc&]\xf1<\x05DsB}C\u0189\a\f\x95\x92\f\xe3%\x00\x00\xe0\x94\xbc\x96\u007f\xe4A\x8c\x18\xb9\x98X\x96m\x87\x06x\u0722\xb8\x88y\x8a\x01\xd9\xcb\u074d~\xd2\x10\x00\x00\u07d4\xbc\x99\x9e8\\Z\xeb\xca\xc8\xd6\xf3\xf0\xd6\rZ\xa7%3m\r\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbc\x9c\x95\u07eb\x97\xa5t\u03a2\xaa\x80;\\\xaa\x19|\xef\f\xff\x89\x16\u012b\xbe\xbe\xa0\x10\x00\x00\u07d4\xbc\x9e\x0e\xc6x\x8f}\xf4\xc7\xfc!\n\xac\xd2 \xc2~E\xc9\x10\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbc\xa3\xff\xd4h?\xba\n\u04fb\xc9\a4\xb6\x11\u069c\xfbE~\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\xae\u042c\xb6\xa7o\x11?|a5U\xa2\u00f0\xf5\xbf4\xa5\x89\n~\xbd^Cc\xa0\x00\x00\u07d4\xbc\xaf4y\x18\xef\xb2\xd6=\xde\x03\xe3\x92u\xbb\xe9}&\xdfP\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbc\xb4\"\xdcM\u04aa\xe9J\xba\xe9^\xa4]\xd1s\x1b\xb6\xb0\xba\x89\x18BO_\v\x1bN\x00\x00\u07d4\xbc\xbd1%.\u0088\xf9\x1e)\x8c\xd8\x12\xc9!`\xe783\x1a\x89k\x1b\xc2\xca\xc0\x9aY\x00\x00\u07d4\xbc\xbfk\xa1f\xe24\r\xb0R\xea#\u0480)\xb0\xdej\xa3\x80\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xbc\xc8E\x97\xb9\x1es\xd5\u0174\u059c\x80\xec\xf1F\x86\x0fw\x9a\x89\xedp\xb5\xe9\xc3\xf2\xf0\x00\x00\u07d4\xbc\xc9Y;-\xa6\xdfj4\xd7\x1b\x1a\xa3\x8d\xac\xf8v\xf9[\x88\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbc\xd9^\xf9bF+n\u07e1\x0f\u0687\xd7\"B\xfe>\xdb\\\x89\x12\x1d\x06\xe1/\xff\x98\x80\x00\u07d4\xbc\u065e\xdc!`\xf2\x10\xa0^:\x1f\xa0\xb0CL\xed\x00C\x9b\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbc\u07ec\xb9\xd9\x02<4\x17\x18.\x91\x00\xe8\xea\x1d73\x93\xa3\x89\x034-`\xdf\xf1\x96\x00\x00\u07d4\xbc\xe1>\"2*\u03f3U\xcd!\xfd\r\xf6\f\xf9:\xdd&\u0189\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xbc\xe4\x04u\xd3E\xb0q-\xeep=\x87\xcdvW\xfc\u007f;b\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\xbc\xed\xc4&|\u02c9\xb3\x1b\xb7d\xd7!\x11q\x00\x8d\x94\xd4M\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbc\xfc\x98\xe5\xc8+j\xdb\x18\n?\xcb\x12\v\x9av\x90\xc8j?\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\x04;g\xc6>`\xf8A\xcc\xca\x15\xb1)\xcd\xfee\x90\xc8\xe3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd\x04\u007f\xf1\xe6\x9c\u01b2\x9a\xd2d\x97\xa9\xa6\xf2z\x90?\xc4\u0749.\xe4IU\b\x98\xe4\x00\x00\u07d4\xbd\b\xe0\xcd\xde\xc0\x97\xdby\x01\ua05a=\x1f\xd9\u0789Q\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbd\t\x12l\x89\x1cJ\x83\x06\x80Y\xfe\x0e\x15ylFa\xa9\xf4\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xbd\f\\\u05d9\xeb\u0106B\xef\x97\xd7N\x8eB\x90d\xfe\u4489\x11\xac(\xa8\xc7)X\x00\x00\u07d4\xbd\x17\xee\xd8+\x9a%\x92\x01\x9a\x1b\x1b<\x0f\xba\xd4\\@\x8d\"\x89\r\x8drkqw\xa8\x00\x00\u07d4\xbd\x18\x037\v\u0771)\xd29\xfd\x16\xea\x85&\xa6\x18\x8a\u5389\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbd+p\xfe\xcc7d\x0fiQO\xc7\xf3@IF\xaa\xd8k\x11\x89A\rXj \xa4\xc0\x00\x00\u07d4\xbd0\x97\xa7\x9b<\r.\xbf\xf0\xe6\xe8j\xb0\xed\xad\xbe\xd4p\x96\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xbd2]@)\xe0\xd8r\x9fm9\x9cG\x82$\xae\x9ez\xe4\x1e\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xbdC*9\x16$\x9bG$):\xf9\x14nI\xb8(\n\u007f*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbdG\xf5\xf7n;\x93\x0f\xd9HR\t\xef\xa0\xd4v=\xa0uh\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbdK`\xfa\xect\n!\xe3\a\x13\x91\xf9j\xa54\xf7\xc1\xf4N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xbdK\u0571\"\xd8\xef{|\x8f\x06gE\x03 \xdb!\x16\x14.\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbdQ\xee.\xa1C\u05f1\u05b7~~D\xbd\xd7\xda\x12\U00105b09G~\x06\u0332\xb9(\x00\x00\u07d4\xbdY\tN\aO\x8dy\x14*\xb1H\x9f\x14\x8e2\x15\x1f \x89\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbdZ\x8c\x94\xbd\x8b\xe6G\x06D\xf7\f\x8f\x8a3\xa8\xa5\\cA\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbd^G:\xbc\xe8\xf9zi2\xf7|/\xac\xaf\x9c\xc0\xa0\x05\x14\x89<\x92X\xa1\x06\xa6\xb7\x00\x00\u07d4\xbd_F\u02ab,=K(\x93\x96\xbb\xb0\u007f <M\xa8%0\x89\x04V9\x18$O@\x00\x00\u07d4\xbdf\xff\xed\xb50\xea\v.\x85m\xd1*\xc2)l1\xfe)\xe0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbdg\xd2\xe2\xf8-\xa8\x86\x13A\xbc\x96\xa2\xc0y\x1f\xdd\xf3\x9e@\x89\n\xd7\xc0yG\xc8\xfb\x00\x00\u07d4\xbdjGMf4[\xcd\xd7\aYJ\xdbc\xb3\fx\"\xafT\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xbdr;(\x9asg\xb6\xec\xe2E^\xd6\x1e\xdbIg\n\xb9\u010a\x01\x0f\f\u07a1d!?\x80\x00\u07d4\xbds\xc3\xcb\xc2j\x17Pb\xea\x03 \u0744\xb2S\xbc\xe6CX\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94\xbdt\x19\xdc*\t\nF\xe2\x87=}\xe6\xea\xaa\u055e\x19\xc4y\x8a\x01p\xbc\xb6qu\x9f\b\x00\x00\u07d4\xbd\x87e\xf4\x12\x99\xc7\xf4y\x92<O\u044f\x12mr)\x04}\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbd\x93\xe5P@>*\x06\x11>\xd4\xc3\xfb\xa1\xa8\x91;\x19@~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\x9eV\xe9\x02\xf4\xbe\x1f\xc8v\x8d\x808\xba\xc6>*\u02ff\x8e\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xbd\xa4\xbe1~~K\xed\x84\xc0I^\xee2\xd6\a\xec8\xcaR\x89}2'yx\xefN\x80\x00\u07d4\xbd\xb6\v\x82:\x11s\xd4Z\a\x92$_\xb4\x96\xf1\xfd3\x01\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xba\xf6CM@\xd65[\x1e\x80\xe4\f\u012b\x9ch\xd9a\x16\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbd\xc0,\xd43\f\x93\xd6\xfb\xdaOm\xb2\xa8]\xf2/C\xc23\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc4aF+c\"\xb4b\xbd\xb3?\"y\x9e\x81\b\xe2A}\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xbd\xc79\xa6\x99p\v.\x8e,JL{\x05\x8a\x0eQ=\u07be\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbd\xc7Hs\xaf\x92+\x9d\xf4t\x85;\x0f\xa7\xff\v\xf8\xc8&\x95\x89\xd8\xc9F\x00c\xd3\x1c\x00\x00\u07d4\xbd\xca*\x0f\xf3E\x88\xafb_\xa8\xe2\x8f\xc3\x01Z\xb5\xa3\xaa\x00\x89~\xd7?w5R\xfc\x00\x00\u07d4\xbd\xd3%N\x1b:m\xc6\xcc,i}Eq\x1a\xca!\xd5\x16\xb2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbd\u07e3M\x0e\xbf\x1b\x04\xafS\xb9\x9b\x82IJ\x9e=\x8a\xa1\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xbd\xe4\xc7?\x96\x9b\x89\xe9\u03aef\xa2\xb5\x18DH\x0e\x03\x8e\x9a\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbd\xe9xj\x84\xe7[H\xf1\x8erm\u05cdp\xe4\xaf>\xd8\x02\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xbd\xed\x11a/\xb5\xc6\u0699\xd1\xe3\x0e2\v\xc0\x99Tf\x14\x1e\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xbd\xed~\a\xd0q\x1ehM\xe6Z\u0232\xabW\xc5\\\x1a\x86E\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xbd\xf6\x93\xf83\xc3\xfeG\x17S\x18G\x88\xebK\xfeJ\xdc?\x96\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbd\xf6\xe6\x8c\f\xd7X@\x80\xe8G\xd7,\xbb#\xaa\xd4j\xeb\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbe\n/8_\t\xdb\xfc\xe9g2\xe1+\xb4\n\xc3I\x87\x1b\xa8\x89WL\x11^\x02\xb8\xbe\x00\x00\u07d4\xbe\f*\x80\xb9\xde\bK\x17(\x94\xa7l\xf4szOR\x9e\x1a\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\x1c\xd7\xf4\xc4r\a\th\xf3\xbd\xe2h6k!\xee\xea\x83!\x89\xe9\x1a|\u045f\xa3\xb0\x00\x00\u07d4\xbe#F\xa2\u007f\xf9\xb7\x02\x04OP\r\xef\xf2\xe7\xff\xe6\x82EA\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xbe$q\xa6\u007f`G\x91\x87r\xd0\xe3h9%^\xd9\u0591\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xbe+\"\x80R7h\xea\x8a\xc3\\\xd9\xe8\x88\xd6\nq\x93\x00\u0509lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe+2nx\xed\x10\xe5P\xfe\xe8\xef\xa8\xf8\a\x03\x96R/Z\x8a\bW\xe0\xd6\xf1\xdav\xa0\x00\x00\xe0\x94\xbe0Zyn3\xbb\xf7\xf9\xae\xaee\x12\x95\x90f\xef\xda\x10\x10\x8a\x02M\xceT\xd3J\x1a\x00\x00\x00\u07d4\xbeG\x8e\x8e=\xdek\xd4\x03\xbb-\x1ce|C\x10\xee\x19'#\x89\x1a\xb2\xcf|\x9f\x87\xe2\x00\x00\u07d4\xbeN}\x98?.*ck\x11\x02\xecp9\xef\xeb\xc8B\u9349\x03\x93\xef\x1aQ'\xc8\x00\x00\u07d4\xbeO\xd0sap\"\xb6\u007f\\\x13I\x9b\x82\u007fv69\xe4\xe3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbeRZ3\xea\x91aw\xf1r\x83\xfc\xa2\x9e\x8b5\v\u007fS\v\x89\x8f\x01\x9a\xafF\xe8x\x00\x00\u07d4\xbeS2/C\xfb\xb5\x84\x94\xd7\xcc\xe1\x9d\xda'+$P\xe8'\x89\n\xd7\u03afB\\\x15\x00\x00\u07d4\xbeS\x82F\xddNo\f \xbfZ\xd17<;F:\x13\x1e\x86\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbeZ`h\x99\x98c\x9a\xd7[\xc1\x05\xa3qt>\xef\x0fy@\x89\x1b2|s\xe1%z\x00\x00\u07d4\xbe\\\xba\x8d7By\x86\xe8\xca&\x00\xe8X\xbb\x03\xc3YR\x0f\x89\xa00\xdc\xeb\xbd/L\x00\x00\u07d4\xbe`\x03~\x90qJK\x91~a\xf1\x93\xd84\x90g\x03\xb1:\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbec:77\xf6\x849\xba\xc7\xc9\nR\x14 X\ue38ao\x894\n\xad!\xb3\xb7\x00\x00\x00\xe0\x94\xbee\x9d\x85\xe7\xc3O\x883\xea\u007fH\x8d\xe1\xfb\xb5\xd4\x14\x9b\xef\x8a\x01\xeb\xd2:\xd9\u057br\x00\x00\u07d4\xbes'M\x8cZ\xa4J<\xbe\xfc\x82c\xc3{\xa1!\xb2\n\u04c9\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xbe\x86\u0430C\x84\x19\u03b1\xa081\x927\xbaR\x06\xd7.F\x8964\xfb\x9f\x14\x89\xa7\x00\x00\u07d4\xbe\x8d\u007f\x18\xad\xfe]l\xc7u9I\x89\xe1\x93\f\x97\x9d\x00}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\x91\x86\xc3JRQJ\xbb\x91\a\x86\x0fgO\x97\xb8!\xbd[\x89\x1b\xa0\x1e\xe4\x06\x03\x10\x00\x00\u07d4\xbe\x93W\x93\xf4[p\xd8\x04]&T\xd8\xdd:\xd2K[a7\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xbe\x98\xa7\u007f\xd4\x10\x97\xb3OY\xd7X\x9b\xaa\xd0!e\x9f\xf7\x12\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xbe\x9b\x8c4\xb7\x8e\xe9G\xff\x81G.\xdaz\xf9\xd2\x04\xbc\x84f\x89\b!\xab\rD\x14\x98\x00\x00\u07d4\xbe\xa0\r\xf1pg\xa4:\x82\xbc\x1d\xae\xca\xfbl\x140\x0e\x89\xe6\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xbe\xa0\xaf\xc9:\xae!\b\xa3\xfa\xc0Yb;\xf8o\xa5\x82\xa7^\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xbe\xb35\x8cP\u03dfu\xff\xc7mD<,\u007fU\aZ\x05\x89\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xbe\xb4\xfd1UYC`E\u0739\x9dI\xdc\xec\x03\xf4\fB\u0709lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xc2\xe6\xde9\xc0|+\xaeUj\u03fe\xe2\xc4r\x8b\x99\x82\xe3\x89\x1f\x0f\xf8\xf0\x1d\xaa\xd4\x00\x00\u07d4\xbe\xc6d\x0fI\t\xb5\x8c\xbf\x1e\x80cB\x96\x1d`u\x95\tl\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xbe\xc8\xca\xf7\xeeIF\x8f\xeeU.\xff:\xc5#N\xb9\xb1}B\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbe\xce\xf6\x1c\x1cD+\xef|\xe0Ks\xad\xb2I\xa8\xba\x04~\x00\x896;V\u00e7T\xc8\x00\x00\u0794\xbe\xd4d\x9d\xf6F\u2052)\x03-\x88hUo\xe1\xe0S\u04c8\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xbe\xd4\xc8\xf0\x06\xa2|\x1e_|\xe2\x05\xdeu\xf5\x16\xbf\xb9\xf7d\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xbe\xe8\u0430\bB\x19T\xf9-\x00\r9\x0f\xb8\xf8\xe6X\xea\xee\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbe\xec\u05af\x90\f\x8b\x06J\xfc\xc6\a?-\x85\u055a\xf1\x19V\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xbe\xef\x94!8y\xe0&\"\x14+\xeaa)\tx\x93\x9a`\u05ca\x016\x85{2\xad\x86\x04\x80\x00\xe0\x94\xbe\xf0}\x97\xc3H\x1f\x9dj\xee\x1c\x98\xf9\xd9\x1a\x18\n2D+\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xbe\xfbD\x8c\f_h?\xb6~\xe5p\xba\xf0\xdbV\x86Y\x97Q\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xbf\x05\a\f,4!\x93\x11\xc4T\x8b&\x14\xa48\x81\r\xedm\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbf\x05\xff^\xcf\r\xf2\u07c8wY\xfb\x82t\xd928\xac&}\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\xbf\t\xd7pH\xe2p\xb6b3\x0e\x94\x86\xb3\x8bC\xcdx\x14\x95\x8a\\S\x9b{\xf4\xff(\x80\x00\x00\u07d4\xbf\x17\xf3\x97\xf8\xf4o\x1b\xaeE\u0447\x14\x8c\x06\xee\xb9Y\xfaM\x896I\u0156$\xbb0\x00\x00\u07d4\xbf\x186A\xed\xb8\x86\xce`\xb8\x19\x02a\xe1OB\xd9<\xce\x01\x89\x01[5W\xf1\x93\u007f\x80\x00\u07d4\xbf*\xeaZ\x1d\xcfn\u04f5\xe829D\xe9\x83\xfe\xdf\u046c\xfb\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xbf@\x96\xbcT}\xbf\xc4\xe7H\t\xa3\x1c\x03\x9e{8\x9d^\x17\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xbfI\xc1H\x981eg\u0637\t\xc2\xe5\x05\x94\xb3f\xc6\u04cc\x89'\xbf8\xc6TM\xf5\x00\x00\u07d4\xbfLs\xa7\xed\xe7\xb1d\xfe\a!\x14\x846T\xe4\xd8x\x1d\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfP\xce.&K\x9f\xe2\xb0h0az\xed\xf5\x02\xb25\x1bE\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfY\xae\xe2\x81\xfaC\xfe\x97\x19CQ\xa9\x85~\x01\xa3\xb8\x97\xb2\x89 \x86\xac5\x10R`\x00\x00\u07d4\xbfh\u048a\xaf\x1e\xee\xfe\xf6F\xb6^\x8c\xc8\u0450\xf6\xc6\u069c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xbfi%\xc0\aQ\x00\x84@\xa6s\x9a\x02\xbf+l\u06ab^:\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xbfw\x01\xfcb%\u0561x\x15C\x8a\x89A\xd2\x1e\xbc]\x05\x9d\x89e\xea=\xb7UF`\x00\x00\u07d4\xbf\x8b\x80\x05\xd66\xa4\x96d\xf7Bu\xefBC\x8a\xcde\xac\x91\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\x92A\x8a\fl1$M\"\x02`\xcb>\x86}\u05f4\xefI\x89\x05i\x00\xd3<\xa7\xfc\x00\x00\u07d4\xbf\x9a\xcdDE\xd9\xc9UF\x89\u02bb\xba\xb1\x88\x00\xff\x17A\u008965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\x9f'\x1fz~\x12\xe3m\xd2\xfe\x9f\xac\xeb\xf3\x85\xfeaB\xbd\x89\x03f\xf8O{\xb7\x84\x00\x00\u07d4\xbf\xa8\xc8X\xdf\x10,\xb1$!\x00\x8b\n1\xc4\xc7\x19\n\xd5`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xbf\xae\xb9\x10ga}\u03cbD\x17+\x02\xafaVt\x83]\xba\x89\b\xb5\x9e\x88H\x13\b\x80\x00\xe0\x94\xbf\xb0\xea\x02\xfe\xb6\x1d\xec\x9e\"\xa5\a\tY3\x02\x99\xc40r\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xbf\xbc\xa4\x18\xd3R\x9c\xb3\x93\b\x10b\x03*n\x11\x83\u01b2\u070a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xbf\xbe\x05\u831c\xbb\xcc\x0e\x92\xa4\x05\xfa\xc1\xd8]\xe2H\xee$\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xbf\xbf\xbc\xb6V\u0099+\xe8\xfc\u0782\x19\xfb\xc5J\xad\u055f)\x8a\x02\x1e\x18\xd2\xc8!\xc7R\x00\x00\u07d4\xbf\xc5z\xa6f\xfa\u239f\x10zI\xcbP\x89\xa4\xe2!Q\u074965\u026d\xc5\u07a0\x00\x00\u07d4\xbf\u02d70$c\x04p\r\xa9\vAS\xe7\x11Ab.\x1cA\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xbf\xd9<\x90\u009c\a\xbc_\xb5\xfcI\xae\xeaU\xa4\x0e\x13O5\x8a\x05\xed\xe2\x0f\x01\xa4Y\x80\x00\x00\xe0\x94\xbf\xe3\xa1\xfcn$\xc8\xf7\xb3%\x05`\x99\x1f\x93\u02e2\u03c0G\x8a\x10\xf0\xcf\x06M\u0552\x00\x00\x00\u07d4\xbf\u6f30\xf0\xc0xRd3$\xaa]\xf5\xfdb%\xab\xc3\u0289\x04\t\xe5+H6\x9a\x00\x00\u07d4\xbf\xf5\xdfv\x994\xb8\x94<\xa9\x13}\x0e\xfe\xf2\xfen\xbb\xb3N\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xbf\xfbi)$\x1fx\x86\x93'>p\"\xe6\x0e>\xab\x1f\xe8O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x06O\x1d\x94t\xab\x91]V\x90l\x9f\xb3 \xa2\xc7\t\x8c\x9b\x89\x13h?\u007f<\x15\xd8\x00\x00\u07d4\xc0\a\xf0\xbd\xb6\xe7\x00\x92\x02\xb7\xaf>\xa9\t\x02i|r\x14\x13\x89\xa2\xa0\xe4>\u007f\xb9\x83\x00\x00\u07d4\xc0\n\xb0\x80\xb6C\xe1\u00ba\xe3c\xe0\u0455\xde.\xff\xfc\x1cD\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u0794\xc0 wD\x9a\x13Jz\xd1\xef~M\x92z\xff\xec\ueb75\xae\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc0$q\xe3\xfc.\xa0S&\x15\xa7W\x1dI2\x89\xc1<6\xef\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0-n\xad\xea\xcf\x1bx\xb3\u0285\x03\\c{\xb1\xce\x01\xf4\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc03\xb12Z\n\xf4Tr\xc2U'\x85;\x1f\x1c!\xfa5\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc03\xbe\x10\xcbHa;\xd5\xeb\xcb3\xedI\x02\xf3\x8bX0\x03\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc04[3\xf4\x9c\xe2\u007f\xe8,\xf7\xc8M\x14\x1ch\xf5\x90\xcev\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0=\xe4*\x10\x9bezd\xe9\"$\xc0\x8d\xc1'^\x80\u0672\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0@i\u07f1\x8b\tlxg\xf8\xbe\xe7zm\xc7Gz\xd0b\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xc0A?Z|-\x9aK\x81\b(\x9e\xf6\xec\xd2qx\x15$\xf4\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xc0C\xf2E-\u02d6\x02\xefb\xbd6\x0e\x03=\xd29q\xfe\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0OK\xd4\x04\x9f\x04F\x85\xb8\x83\xb6)Y\xaec\x1df~5\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0V\u053dk\xf3\u02ec\xace\xf8\xf5\xa0\xe3\x98\v\x85'@\xae\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0[t\x06 \xf1s\xf1nRG\x1d\u00cb\x9cQJ\v\x15&\x89\a\x96\xe3\xea?\x8a\xb0\x00\x00\u07d4\xc0i\xef\x0e\xb3B\x99\xab\xd2\xe3-\xab\xc4yD\xb2r3H$\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\xc0l\xeb\xbb\xf7\xf5\x14\x9af\xf7\xeb\x97k>G\xd5e\x16\xda/\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc0r^\u00bd\xc3:\x1d\x82`q\u07a2\x9db\xd48Z\x8c%\x8a\b\xa0\x85\x13F:\xa6\x10\x00\x00\u07d4\xc0~8g\xad\xa0\x96\x80z\x05\x1al\x9c4\xcc;?J\xd3J\x89`\xf0f \xa8IE\x00\x00\u07d4\xc0\x89^\xfd\x05m\x9a:\x81\xc3\xdaW\x8a\xda1\x1b\xfb\x93V\u03c9\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x90\xfe#\xdc\xd8k5\x8c2\xe4\x8d*\xf9\x10$%\x9fef\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc0\x9af\x17*\xea7\r\x9ac\xda\x04\xffq\xff\xbb\xfc\xff\u007f\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\x9e<\xfc\x19\xf6\x05\xff>\xc9\xc9\xc7\x0e%@\xd7\xee\x97Cf\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc0\xa0*\xb9N\xbeV\xd0E\xb4\x1bb\x9b\x98F.:\x02J\x93\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xc0\xa3\x93\b\xa8\x0e\x9e\x84\xaa\xaf\x16\xac\x01\xe3\xb0\x1dt\xbdk-\x89\afM\xddL\x1c\v\x80\x00\u07d4\xc0\xa6\u02edwi*=\x88\xd1A\xefv\x9a\x99\xbb\x9e<\x99Q\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xc0\xa7\xe8C]\xff\x14\xc2Uws\x9d\xb5\\$\u057fW\xa3\u064a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xc0\xae\x14\xd7$\x83./\xce'x\xde\u007f{\x8d\xaf{\x12\xa9>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc0\xaf\xb7\u0637\x93p\xcf\xd6c\u018c\u01b9p*7\u035e\xff\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc0\xb0\xb7\xa8\xa6\xe1\xac\xdd\x05\xe4\u007f\x94\xc0\x96\x88\xaa\x16\u01ed\x8d\x89\x03{m\x02\xacvq\x00\x00\xe0\x94\xc0\xb3\xf2D\xbc\xa7\xb7\xde[H\xa5>\u06dc\xbe\xab\vm\x88\xc0\x8a\x01;\x80\xb9\x9cQ\x85p\x00\x00\u07d4\xc0\xc0M\x01\x06\x81\x0e>\xc0\xe5J\x19\U000ab157\xe6\x9aW=\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xc0\xca2w\x94.tE\x87K\xe3\x1c\xeb\x90)rqO\x18#\x89\r\x8drkqw\xa8\x00\x00\u07d4\xc0\u02ed<\xcd\xf6T\xda\"\xcb\xcf\\xe\x97\xca\x19U\xc1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc0\xcb\xf6\x03/\xa3\x9e|F\xffw\x8a\x94\xf7\xd4E\xfe\"\xcf0\x89\x10\xce\x1d=\x8c\xb3\x18\x00\x00\u07d4\xc0\xe0\xb9\x03\b\x8e\fc\xf5=\xd0iWTR\xaf\xf5$\x10\u00c9\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc0\xe4W\xbdV\xec6\xa1$k\xfa20\xff\xf3\x8eY&\xef\"\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xc0\xed\rJ\xd1\r\xe045\xb1S\xa0\xfc%\xde;\x93\xf4R\x04\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xc0\xf2\x9e\xd0\af\x11\xb5\xe5^\x13\x05G\xe6\x8aH\xe2m\xf5\u4262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc1\x13(x#\\]\u06e5\xd9\xf3\"\x8bR6\xe4p \xdco\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1\x17\r\xba\xad\xb3\xde\xe6\x19\x8e\xa5D\xba\xec\x93%\x18`\xfd\xa5\x89A\rXj \xa4\xc0\x00\x00\xe0\x94\xc1&W=\x87\xb0\x17ZR\x95\xf1\xdd\a\xc5u\u03cc\xfa\x15\xf2\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc1'\xaa\xb5\x90e\xa2\x86D\xa5k\xa3\xf1^.\xac\x13\xda)\x95\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xc1+\u007f@\u07da/{\xf9\x83f\x14\"\xab\x84\xc9\xc1\xf5\bX\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc1,\xfb{=\xf7\x0f\xce\xca\x0e\xde&5\x00\xe2xs\xf8\xed\x16\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc1/\x88\x1f\xa1\x12\xb8\x19\x9e\xcb\xc7>\xc4\x18W\x90\xe6\x14\xa2\x0f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc18Lnq~\xbeK#\x01NQ\xf3\x1c\x9d\xf7\xe4\xe2[1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1C\x8c\x99\xddQ\xef\x1c\xa88j\xf0\xa3\x17\xe9\xb0AEx\x88\x89\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xc1c\x12(\xef\xbf*.:@\x92\xee\x89\x00\xc69\xed4\xfb\u02093\xc5I\x901r\f\x00\x00\u07d4\xc1u\xbe1\x94\xe6iB-\x15\xfe\xe8\x1e\xb9\xf2\xc5lg\xd9\u0249\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\x82v\x86\xc0\x16\x94\x85\xec\x15\xb3\xa7\xc8\xc0\x15\x17\xa2\x87M\xe1\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xc1\x8a\xb4g\xfe\xb5\xa0\xaa\xdf\xff\x91#\x0f\xf0VFMx\xd8\x00\x89lk\x93[\x8b\xbd@\x00\x00\u0794\xc1\x95\x05CUM\x8aq0\x03\xf6b\xbba,\x10\xadL\xdf!\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xc1\xa4\x1aZ'\x19\x92&\xe4\xc7\xeb\x19\x8b\x03\x1bY\x19o\x98B\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xc1\xb2\xa0\xfb\x9c\xadE\xcdi\x91\x92\xcd'T\v\x88\xd38By\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc1\xb2\xaa\x8c\xb2\xbfb\xcd\xc1:G\xec\xc4e\u007f\xac\xaa\x99_\x98\x8967\x93\xfa\x96\u6980\x00\u07d4\xc1\xb5\x00\x01\x1c\xfb\xa9]|\xd66\xe9^l\xbfagFK%\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc1\xb9\xa5pM5\x1c\xfe\x98?y\xab\xee\xc3\u06fb\xae;\xb6)\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xcb\xd2\xe23*RL\xf2\x19\xb1\r\x87\x1c\xcc \xaf\x1f\xb0\xfa\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xc1\xcd\xc6\x01\xf8\x9c\x04(\xb3\x13\x02\u0447\xe0\xdc\b\xad}\x1cW\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xc1\u052f8\xe9\xbay\x90@\x89HI\xb8\xa8!\x93u\xf1\xacx\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc1\xe1@\x9c\xa5,%CQ4\xd0\x06\u00a6\xa8T-\xfbrs\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xc1\xeb\xa5hJ\xa1\xb2L\xbac\x15\x02c\xb7\xa9\x13\x1a\xee\u008d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc1\xec\x81\xdd\x12=K|-\u0674\xd48\xa7\a,\x11\u0707L\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc1\xf3\x9b\xd3]\xd9\xce\xc37\xb9oG\xc6w\x81\x81`\xdf7\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xc1\xff\xad\a\u06d6\x13\x8cK*S\x0e\xc1\xc7\xde)\xb8\xa0Y,\x88\xf4?\xc2\xc0N\xe0\x00\x00\xe0\x94\xc2\x1f\xa6d:\x1f\x14\xc0)\x96\xadqD\xb7Y&\xe8~\xcbK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc24\nL\xa9L\x96x\xb7IL<\x85%(\xed\xe5\xeeR\x9f\x89\x02\xa3k\x05\xa3\xfd|\x80\x00\u07d4\xc29\xab\u07ee>\x9a\xf5E\u007fR\xed+\x91\xfd\n\xb4\xd9\xc7\x00\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2;/\x92\x1c\xe4\xa3z%\x9e\u4b4b!X\xd1]fOY\x89\x01`\x89\x95\xe8\xbd?\x80\x00\u07d4\xc2C\x99\xb4\xbf\x86\xf73\x8f\xbfd^;\"\xb0\u0dd79\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2L\u03bc#D\xcc\xe5d\x17\xfbhL\xf8\x16\x13\xf0\xf4\xb9\xbd\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc2Rf\xc7gf2\xf1>\xf2\x9b\xe4U\ud50a\xddVw\x92\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\\\xf8&U\f\x8e\xaf\x10\xaf\"4\xfe\xf9\x04\u0779R\x13\xbe\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc2f?\x81E\xdb\xfe\xc6\xc6F\xfc\\I\x96\x13E\xde\x1c\x9f\x11\x89%g\xacp9+\x88\x00\x00\u07d4\xc2pEh\x854+d\vL\xfc\x1bR\x0e\x1aTN\xe0\xd5q\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc2sv\xf4]!\xe1^\xde;&\xf2e_\xce\xe0,\xcc\x0f*\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc2w\x97q\xf0Smy\xa8p\x8fi1\xab\xc4K05\u964a\x04<O\x83\x00\u0733H\x00\x00\u07d4\xc2\u007fN\b\t\x9d\x8c\xf3\x9e\xe1\x16\x01\x83\x8e\xf9\xfc\x06\xd7\xfcA\x89a\t=|,m8\x00\x00\u07d4\u0082\xe6\x99?\xbez\x91.\xa0G\x15?\xfd\x92t'\x0e([\x89\a\x96\v3\x12Gc\x80\x00\u07d4\u0083a\x88\u0662\x92S\xe0\xcb\xdaeq\xb0X\u0089\xa0\xbb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u00aat\x84~\x86\xed\xfd\xd3\xf3\xdb\"\xf8\xa2\x15/\xee\xe5\xb7\xf7\x89o\x11\x88\x86\xb7\x84\xa2\x00\x00\xe0\x94\u00b2\xcb\xe6[\xc6\xc2\xeez<u\xb2\xe4|\x18\x9c\x06.\x8d\x8b\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\u00ba\xe4\xa23\xc2\xd8W$\xf0\u06be\xbd\xa0$\x9d\x83>7\u04ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc2\xc1>r\xd2h\xe7\x15\r\u01d9\xe7\xc6\xcf\x03\u0209T\xce\u05c9%\xf2s\x93=\xb5p\x00\x00\u07d4\xc2\xcb\x1a\xda]\xa9\xa0B8s\x81G\x93\xf1aD\xef6\xb2\xf3\x89HU~;p\x17\xdf\x00\x00\u07d4\xc2\xd1w\x8e\xf6\xee_\xe4\x88\xc1E\xf3Xkn\xbb\xe3\xfb\xb4E\x89>\x1f\xf1\xe0;U\xa8\x00\x00\xe0\x94\xc2\xd9\xee\xdb\xc9\x01\x92c\xd9\xd1l\u016e\a-\x1d=\xd9\xdb\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc2\xe0XJq4\x8c\xc3\x14\xb7; )\xb6#\v\x92\u06f1\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc2\xe2\u0518\xf7\r\xcd\bY\xe5\v\x02:q\nmK!3\xbd\x8989\x11\xf0\f\xbc\xe1\x00\x00\u07d4\xc2\xed_\xfd\u046d\xd8U\xa2i/\xe0b\xb5\xd6\x18t#`\u0509A\rXj \xa4\xc0\x00\x00\u07d4\xc2\xee\x91\xd3\xefX\xc9\u0465\x89\x84N\xa1\xae1%\xd6\u017ai\x894\x95tD\xb8@\xe8\x00\x00\u07d4\xc2\xfa\xfd\xd3\n\xcbmg\x06\xe9)<\xb0&A\xf9\xed\xbe\a\xb5\x89Q\x00\x86\vC\x0fH\x00\x00\u07d4\xc2\xfd\v\xf7\xc7%\xef>\x04~Z\xe1\u009f\xe1\x8f\x12\xa7)\x9c\x89Hz\x9a0E9D\x00\x00\u07d4\xc2\xfe}us\x1fcm\xcd\t\xdb\xda\x06q9;\xa0\xc8*}\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\xc3\x10z\x9a\xf32-R8\xdf\x012A\x911b\x959W}\x89\x1a\xb4\xe4d\xd4\x141\x00\x00\xe0\x94\xc3\x11\v\xe0\x1d\xc9sL\xfcn\x1c\xe0\u007f\x87\xd7}\x13E\xb7\xe1\x8a\x01\x0f\f\xe9I\xe0\x0f\x93\x00\x00\u07d4\xc3 8\xcaR\xae\xe1\x97E\xbe\\1\xfc\xdcT\x14\x8b\xb2\xc4\u0409\x02\xb5\xaa\xd7,e \x00\x00\u07d4\xc3%\xc3R\x80\x1b\xa8\x83\xb3\"l_\xeb\r\xf9\xea\xe2\xd6\xe6S\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xc3.\xc7\xe4*\xd1l\xe3\xe2UZ\xd4\xc5C\x06\xed\xa0\xb2gX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc32\xdfP\xb1<\x014\x90\xa5\xd7\xc7]\xbf\xa3f\u0687\xb6\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc3:\u0373\xba\x1a\xab'P{\x86\xb1]g\xfa\xf9\x1e\xcfb\x93\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3>\u0393Z\x8fN\xf98\xea~\x1b\xac\x87\u02d2]\x84\x90\u028a\a\x03\x8c\x16x\x1fxH\x00\x00\u07d4\xc3@\xf9\xb9\x1c&r\x8c1\xd1!\xd5\xd6\xfc;\xb5m=\x86$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3F\xcb\x1f\xbc\xe2\xab(]\x8eT\x01\xf4-\xd7#M7\xe8m\x89\x04\x86\u02d7\x99\x19\x1e\x00\x00\xe0\x94\xc3H=n\x88\xac\x1fJ\xe7<\xc4@\x8dl\x03\xab\xe0\xe4\x9d\u028a\x03\x99\x92d\x8a#\u0220\x00\x00\xe0\x94\xc3H\xfcZF\x13#\xb5{\xe3\x03\u02c96\x1b\x99\x19\x13\xdf(\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc3N;\xa12.\xd0W\x11\x83\xa2O\x94 N\xe4\x9c\x18fA\x89\x03'\xaf\uf927\xbc\x00\x00\xe0\x94\xc3[\x95\xa2\xa3s|\xb8\xf0\xf5\x96\xb3E$\x87+\xd3\r\xa24\x8a\x01\x98\xbe\x85#^-P\x00\x00\xe0\x94\xc3c\x1cv\x98\xb6\xc5\x11\x19\x89\xbfE''\xb3\xf99Zm\xea\x8a\x02C'X\x96d\x1d\xbe\x00\x00\u07d4\xc3l\vc\xbf\xd7\\/\x8e\xfb\x06\b\x83\xd8h\xcc\xcdl\xbd\xb4\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xc3uk\xcd\xcc~\xect\xed\x89j\xdf\xc35'Y0&n\b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u00c4\xacn\xe2|9\xe2\xf2x\xc2 \xbd\xfa[\xae\xd6&\xd9\u04c9 \x86\xac5\x10R`\x00\x00\u07d4\u00e0F\xe3\u04b2\xbfh\x14\x88\x82n2\xd9\xc0aQ\x8c\xfe\x8c\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\u07d4\u00e9\"j\xe2u\xdf,\xab1+\x91\x10@cJ\x9c\x9c\x9e\xf6\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u00f9(\xa7o\xadex\xf0O\x05U\xe69R\xcd!\xd1R\n\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xc2)s)\xa6\xfd\x99\x11~T\xfcj\xf3y\xb4\xd5VT~\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc3\xc3\xc2Q\rg\x80 HZcs]\x13\a\xecL\xa60+\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc3\xcbk6\xafD?,n%\x8bJ9U:\x81\x87G\x81\x1f\x89WG=\x05\u06ba\xe8\x00\x00\xe0\x94\xc3\xdbVW\xbbr\xf1\rX\xf21\xfd\xdf\x11\x98\n\xffg\x86\x93\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xc3\u06df\xb6\xf4lH\n\xf3De\u05d7S\xb4\xe2\xb7Jg\u038a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc3\xddX\x908\x860;\x92\x86%%z\xe1\xa0\x13\xd7\x1a\xe2\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe0G\x1cd\xff5\xfaR2\xcc1!\xd1\u04cd\x1a\x0f\xb7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\xe2\f\x96\u07cdN8\xf5\v&Z\x98\xa9\x06\xd6\x1b\xc5\x1aq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc3\u31f0<\xe9\\\xcf\xd7\xfaQ\u0744\x01\x83\xbcCS(\t\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc3\xf8\xf6r\x95\xa5\xcd\x04\x93d\xd0]#P&#\xa3\xe5.\x84\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc4\x01\xc4'\xcc\xcf\xf1\r\xec\xb8d /6\xf5\x80\x83\"\xa0\xa8\x89\xb4{Q\xa6\x9c\xd4\x02\x00\x00\u07d4\xc4\b\x8c\x02_>\x85\x01?T9\xfb4@\xa1s\x01\xe5D\xfe\x89~\t\xdbM\x9f?4\x00\x00\u07d4\xc4\x14a\xa3\u03fd2\u0246UU\xa4\x8117\xc0v1#`\x8965\xc6 G9\u0640\x00\u07d4\xc4 8\x8f\xbe\xe8J\xd6V\xddh\xcd\xc1\xfb\xaa\x93\x92x\v4\x89\n-\xcac\xaa\xf4\u0140\x00\u07d4\xc4\"P\xb0\xfeB\xe6\xb7\xdc\xd5\u0210\xa6\xf0\u020f__\xb5t\x89\b\x1e\xe4\x82SY\x84\x00\x00\u07d4\xc4-j\xebq\x0e:P\xbf\xb4Ml1\t)i\xa1\x1a\xa7\xf3\x89\b\"c\xca\xfd\x8c\xea\x00\x00\xe0\x94\xc4@\xc7\xca/\x96Kir\xeffJ\"a\xdd\xe8\x92a\x9d\x9c\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xc4K\xde\xc8\xc3l\\h\xba\xa2\xdd\xf1\xd41i2)rlC\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xc4OJ\xb5\xbc`9|s~\xb0h3\x91\xb63\xf8<H\xfa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc4R\xe0\xe4\xb3\u05ae\x06\xb86\xf02\xca\t\xdb@\x9d\xdf\xe0\xfb\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xc4Z\x1c\xa1\x03k\x95\x00A\x87\u036cD\xa3n3\xa9J\xb5\u00c9\r\xd0\x0fr\x03\x01\x88\x00\x00\u07d4\xc4]G\xab\f\x9a\xa9\x8a[\xd6-\x16\">\xa2G\x1b\x12\x1c\xa4\x89 .h\xf2\u00ae\xe4\x00\x00\u07d4\xc4h\x1es\xbb\x0e2\xf6\xb7& H1\xffi\xba\xa4\x87~2\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xc4k\xbd\xefv\xd4\xca`\xd3\x16\xc0\u007f]\x1ax\x0e;\x16_~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc4}a\v9\x92P\xf7\x0e\xcf\x13\x89\xba\xb6),\x91&O#\x89\x0f\xa7\xe7\xb5\xdf<\xd0\x00\x00\u07d4\u0100;\xb4\a\xc7b\xf9\vu\x96\xe6\xfd\u1513\x1ev\x95\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0106Q\xc1\xd9\xc1k\xffL\x95T\x88l??&C\x1foh\x89#\xab\x95\x99\xc4?\b\x00\x00\u07d4\u0109\xc8?\xfb\xb0%*\xc0\xdb\xe3R\x12\x17c\x0e\x0fI\x1f\x14\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u010bi<\xac\xef\xdb\xd6\xcb]x\x95\xa4.1\x962~&\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0113H\x9eV\u00fd\xd8)\x00}\xc2\xf9VA)\x06\xf7k\xfa\x89\x02\xa7\x91H\x8eqT\x00\x00\u07d4\u0116\u02f0E\x9aj\x01`\x0f\u0149\xa5Z2\xb4T!\u007f\x9d\x89\x0e\u0683\x8cI)\b\x00\x00\u07d4\u011c\xfa\xa9g\xf3\xaf\xbfU\x03\x10a\xfcL\xef\x88\xf8]\xa5\x84\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0136\xe5\xf0\x9c\xc1\xb9\r\xf0x\x03\xce=M\x13vj\x9cF\xf4\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\u013e\xc9c\b\xa2\x0f\x90\u02b1\x83\x99\u0113\xfd=\x06Z\xbfE\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xc4\xc0\x1a\xfc>\x0f\x04R!\xda\x12\x84\u05c7\x85tD/\xb9\xac\x8a\x01\x92<h\x8bs\xab\x04\x00\x00\u07d4\xc4\xc1S\x18\xd3p\xc73\x18\xcc\x18\xbd\xd4f\u06eaLf\x03\xbf\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xc4\xc6\xcbr=\u05ef\xa7\xebSV\x15\xe5?<\xef\x14\xf1\x81\x18\x89lk\x8f\xce\r\x18y\x80\x00\u07d4\xc4\xccE\xa2\xb6<'\xc0\xb4B\x9eX\xcdB\xdaY\xbes\x9b\u058965\u026d\xc5\u07a0\x00\x00\u07d4\xc4\u03d3\x0e]\x11j\xb8\xd1;\x9f\x9a~\u012bP\x03\xa6\xab\u0789\x11X\xe4`\x91=\x00\x00\x00\u07d4\xc4\xd9\x16WNh\u011f~\xf9\xd3\xd8-\x168\xb2\xb7\xee\t\x85\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94\xc4\xda\u0168\xa0&O\xbc\x10U9\x1cP\x9c\xc3\xee!\xa6\xe0L\x8a\x01`k\u007f\xa09\xcet\x00\x00\u07d4\xc4\xdd\x04\x8b\xfb\x84\x0e+\xc8\\\xb5?\xcbu\xab\xc4C\xc7\xe9\x0f\x89\xc9q\xdc\a\xc9\u01d0\x00\x00\u07d4\xc4\xf2\x91;&\\C\x0f\xa1\xab\x8a\xdf&\xc33\xfc\x1d\x9bf\xf2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc4\xf7\xb1:\xc6\xd4\xebM\xb3\xd4\xe6\xa2R\xaf\x8a\a\xbdYW\u0689\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc4\xf7\xd2\xe2\xe2 \x84\xc4Op\xfe\xaa\xb6\xc3!\x05\xf3\xda7o\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xc4\xffo\xbb\x1f\t\xbd\x9e\x10+\xa03\xd66\xac\x1cL\x0fS\x04\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc4\xff\xad\xaa\xf2\x82?\xbe\xa7\xbf\xf7\x02\x02\x1b\xff\u0105>\xb5\u0249\x02J\x19\xc1\xbdo\x12\x80\x00\xe0\x94\xc5\x00\xb7 sN\xd2)8\u05cc^H\xb2\xba\x93g\xa5u\xba\x8a\a\x12\x9e\x1c\xdf7>\xe0\x00\x00\u07d4\xc5\x0f\xe4\x15\xa6A\xb0\x85lNu\xbf\x96\x05\x15D\x1a\xfa5\x8d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\x13L\xfb\xb1\xdfz \xb0\xedpWb.\xee\u0480\x94}\xad\x89\xcd\xff\x97\xfa\xbc\xb4`\x00\x00\xe0\x94\xc5\x17\xd01\\\x87\x88\x13\xc7\x17\u132f\xa1\xea\xb2eN\x01\u068a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc5\x18y\x9aY%Wb\x13\xe2\x18\x96\xe0S\x9a\xbb\x85\xb0Z\xe3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\"\xe2\x0f\xbf\x04\xed\u007fk\x05\xa3{G\x18\xd6\xfc\xe0\x14.\x1a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc5$\bmF\xc8\x11+\x12\x8b/\xafo|}\x81`\xa88l\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc5-\x1a\fs\u00a1\xbe\x84\x91Q\x85\xf8\xb3O\xaa\n\xdf\x1d\xe3\x89K\xe4\xea\xb3\xfa\x0f\xa6\x80\x00\xe0\x94\xc55\x94\xc7\u03f2\xa0\x8f(L\xc9\u05e6;\xbd\xfc\v1\x972\x8a\nk#(\xff:b\xc0\x00\x00\u07d4\xc57I(\xcd\xf1\x93pTC\xb1L\xc2\r\xa4#G<\xd9\u03c9\a}\x10P\x9b\xb3\xaf\x80\x00\u07d4\xc58\xa0\xff(*\xaa_Ku\u03f6,p\x03~\xe6}O\xb5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5;P\xfd;+r\xbclC\v\xaf\x19JQU\x85\u04d8m\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xc5=y\xf7\u02dbp\x95/\xd3\x0f\xceX\xd5K\x9f\vY\xf6G\x8a\x01\x13\xe2\xd6tCE\xf8\x00\x00\u07d4\xc5I\u07c3\xc6\xf6^\xec\x0f\x1d\u0260\x93J\\_:P\xfd\x88\x89\x9d\xc0\\\xce(\u00b8\x00\x00\u07d4\xc5P\x05\xa6\xc3~\x8c\xa7\xe5C\xce%\x99s\xa3\xca\u0396\x1aJ\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5U\xb91V\xf0\x91\x01#<o|\xf6\xeb<O\x19m3F\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xc5ZkGa\xfd\x11\xe8\xc8_\x15\x17Mtv|\u063d\x9ah\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xc5nkb\xban@\xe5*\xab\x16}!\xdf\x02]\x00UuK\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5s\xe8A\xfa\b\x17J \x8b\x06\f\xcb{L\rv\x97\x12\u007f\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xc5v\x12\u0791\x11\fH.oP[\xcd#\xf3\xc5\x04}\x1da\x89\xc2\x12z\xf8X\xdap\x00\x00\u07d4\u01443\x99\xd1P\x06k\xf7\x97\x9c4\xba)F 6\x8a\xd7\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u014b\x9c\xc6\x1d\xed\xbb\x98\xc3?\"M'\x1f\x0e\"\x8bX43\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\u014fb\xfe\xe9q\x1ej\x05\xdc\t\x10\xb6\x18B\n\xa1'\xf2\x88\x89\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\u0153\xb5F\xb7i\x87\x10\xa2\x05\xadF\x8b,\x13\x15\"\x19\xa3B\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94\u0153\xd6\xe3}\x14\xb5fd:\xc4\x13_$<\xaa\a\x87\xc1\x82\x8a\x02\x8a\x85t%Fo\x80\x00\x00\xe0\x94\u0163\xb9\x8eE\x93\xfe\xa0\xb3\x8cOEZPe\xf0Q\xa2\xf8\x15\x8a\x04L\xf4h\xaf%\xbfw\x00\x00\u07d4\u0164\x8a\x85\x00\xf9\xb4\xe2/\x0e\xb1loFIhvt&}\x89,\x0e\xc5\x03\x85\x04>\x80\x00\xe0\x94\u0166)\xa3\x96%R\u02ce\xde\u0609cj\xaf\xbd\f\x18\xcee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u016e\x86\xb0\xc6\xc7\xe3\x90\x0f\x13h\x10\\VS\u007f\xaf\x8dt>\x89\n1\x06+\xee\xedp\x00\x00\u07d4\u0170\t\xba\xea\xf7\x88\xa2v\xbd5\x81:\xd6[@\v\x84\x9f;\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0175l\xd24&|(\xe8\x9cok\"f\xb0\x86\xa1/\x97\f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xc5\u01a4\x99\x8a3\xfe\xb7dCz\x8b\xe9)\xa7;\xa3J\ad\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\xe0\x94\xc5\xc7=a\xcc\xe7\xc8\xfeL\x8f\xce)\xf3\x90\x92\xcd\x19>\x0f\xff\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xc5\xc7Y\vV!\xec\xf85\x85\x88\u079bh\x90\xf2baC\U000498a1]\tQ\x9b\xe0\x00\x00\u07d4\xc5\xcd\xce\xe0\xe8]\x11}\xab\xbfSj?@i\xbfD?T\xe7\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xc5\u050c\xa2\xdb/\x85\xd8\xc5U\xcb\x0e\x9c\xfe\x82i6x?\x9e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc5\xde\x12\x03\xd3\xcc,\xea1\xc8.\xe2\xdeY\x16\x88\a\x99\xea\xfd\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xc5\xe4\x88\xcf+Vw\x939q\xf6L\xb8 -\xd0WR\xa2\xc0\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc5\xe8\x12\xf7o\x15\xf2\xe1\xf2\xf9\xbcH#H<\x88\x04cog\x89\x03\xf5\x14\x19:\xbb\x84\x00\x00\u07d4\xc5\u94d34\xf1%.\u04ba&\x81D\x87\xdf\u0498+1(\x89\x03\xcbq\xf5\x1f\xc5X\x00\x00\u07d4\xc5\xebB)^\x9c\xad\xea\xf2\xaf\x12\xde\u078a\x8dS\xc5y\xc4i\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xc5\xed\xbb\xd2\xca\x03WeJ\xd0\xeaG\x93\xf8\xc5\xce\xcd0\xe2T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc5\xf6K\xab\xb7\x031B\xf2\x0eF\u05eab\x01\xed\x86\xf6q\x03\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc5\xf6\x87qrF\u068a \r \xe5\u9f2c`\xb6\u007f8a\x89\x01\x8d\x99?4\xae\xf1\x00\x00\u07d4\xc6\x04[<5\vL\xe9\xca\fkuO\xb4\x1ai\xb9~\x99\x00\x892$\xf4'#\xd4T\x00\x00\u07d4\xc6\v\x04eN\x00;F\x83\x04\x1f\x1c\xbdk\u00cf\xda|\xdb\u0589lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x14F\xb7T\xc2N;\x16B\xd9\xe5\x17e\xb4\xd3\xe4k4\xb6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\x18R\x13!\xab\xaf[&Q:J\x95(\bo\"\n\xdco\x89\x01v\xb3D\xf2\xa7\x8c\x00\x00\u07d4\xc6#FW\xa8\a8A&\xf8\x96\x8c\xa1p\x8b\xb0{\xaaI<\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6%\xf8\u024d'\xa0\x9a\x1b\u02bdQ(\xb1\u00a9HV\xaf0\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc65^\xc4v\x8cp\xa4\x9a\xf6\x95\x13\u0343\xa5\xbc\xa7\xe3\xb9\u034a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc6:\xc4\x17\x99.\x9f\x9b`8n\xd9S\xe6\xd7\xdf\xf2\xb0\x90\xe8\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xc6<\u05c8!\x18\xb8\xa9\x1e\aML\x8fK\xa9\x18Q0;\x9a\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xc6R\x87\x1d\x19$\"\u01bc#_\xa0c\xb4J~\x1dC\u3149\bg\x0e\x9e\xc6Y\x8c\x00\x00\xe0\x94\xc6gD\x1e\u007f)y\x9a\xbaadQ\xd5;?H\x9f\x9e\x0fH\x8a\x02\xf2\x9a\xceh\xad\u0740\x00\x00\u07d4\xc6j\xe4\xce\xe8\u007f\xb352\x19\xf7\u007f\x1dd\x86\u0140(\x032\x89\x01\x9a\x16\xb0o\xf8\xcb\x00\x00\u07d4\xc6t\xf2\x8c\x8a\xfd\a?\x8by\x96\x91\xb2\xf0XM\xf9B\xe8D\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0197\xb7\x04w\u02b4.+\x8b&f\x81\xf4\xaesu\xbb%A\x8a\x01.W2\xba\xba\\\x98\x00\x00\u07d4\u019b\x85U9\xce\x1b\x04qG(\xee\xc2Z7\xf3g\x95\x1d\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u019b\xe4@\x13Mb\x80\x98\x01D\xa9\xf6M\x84t\x8a7\xf3I\x89&\u009eG\u0104L\x00\x00\u07d4\u019df<\x8d`\x90\x83\x91\xc8\xd26\x19\x153\xfd\xf7wV\x13\x89\x1aJ\xba\"\\ t\x00\x00\u0794\u01a2\x86\xe0e\xc8_:\xf7H\x12\xed\x8b\u04e8\xce]%\xe2\x1d\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\u01a3\x0e\xf5\xbb3 \xf4\r\xc5\xe9\x81#\rR\xae:\xc1\x93\"\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\u01ae(}\xdb\xe1\x14\x9b\xa1m\xdc\xcaO\xe0j\xa2\uaa48\xa9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xc7\xc1\x917\x98\x97\u075c\x9d\x9a3\x83\x9cJ_b\xc0\x89\r\x89\xd8\xd8T\xb2$0h\x80\x00\xe0\x94\xc6\xcdh\xec56,Z\xd8L\x82\xadN\xdc#!%\x91-\x99\x8a\x05\xe0T\x9c\x962\xe1\xd8\x00\x00\u07d4\xc6\u0615N\x8f?\xc53\xd2\xd20\xff\x02\\\xb4\xdc\xe1O4&\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xc6\xdb\u06de\xfd^\xc1\xb3xn\x06q\xeb\"y\xb2S\xf2\x15\xed\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc6\xdf u\xeb\xd2@\xd4Hi\u00bek\u07c2\xe6=N\xf1\xf5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc6\xe2\xf5\xaf\x97\x9a\x03\xfdr:\x1bn\xfar\x83\x18\u03dc\x18\x00\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xc6\xe3$\xbe\xeb[6v^\xcdFB`\xf7\xf2`\x06\xc5\xc6.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc6\xe4\xcc\fr\x83\xfc\x1c\x85\xbcH\x13\xef\xfa\xafr\xb4\x98#\xc0\x89\x0f\x03\x1e\xc9\xc8}\xd3\x00\x00\xe0\x94\xc6\xee5\x93B)i5)\xdcA\u067bq\xa2IfX\xb8\x8e\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xc6\xfb\x1e\xe3t\x17\u0400\xa0\xd0H\x92;\u06ba\xb0\x95\xd0w\u0189\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc7\x05'\xd4D\u0110\xe9\xfc?\\\xc4Nf\xebO0k8\x0f\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\r\x85mb\x1e\xc1E0<\nd\x00\xcd\x17\xbb\xd6\xf5\xea\xf7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xc7\x0f\xa4Uv\xbf\x9c\x86_\x988\x93\x00,AI&\xf6\x10)\x89\x15\xb4\xaa\x8e\x97\x02h\x00\x00\u07d4\xc7\x11E\xe5)\u01e7\x14\xe6y\x03\xeeb\x06\xe4\xc3\x04+g'\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xc7\x1b*=q5\u04a8_\xb5\xa5q\u073ei^\x13\xfcC\u034965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\x1f\x1du\x87?3\u0732\xddK9\x87\xa1-\a\x91\xa5\xce'\x897\b\xba\xed=h\x90\x00\x00\u07d4\xc7\x1f\x92\xa3\xa5J{\x8c/^\xa4C\x05\xfc\u02c4\xee\xe21H\x89\x02\xb5\x9c\xa11\xd2\x06\x00\x00\u07d4\xc7!\xb2\xa7\xaaD\xc2\x12\x98\xe8P9\xd0\x0e.F\x0eg\v\x9c\x89\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xc7,\xb3\x01%\x8e\x91\xbc\b\x99\x8a\x80]\u0452\xf2\\/\x9a5\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc76\x8b\x97\t\xa5\xc1\xb5\x1c\n\xdf\x18ze\xdf\x14\xe1+}\xba\x8a\x02\x02o\xc7\u007f\x03\u5b80\x00\u07d4\xc79%\x9e\u007f\x85\xf2e\x9b\xef_`\x9e\xd8k=Yl \x1e\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc7>!\x12(\"\x15\xdc\ab\xf3+~\x80}\xcd\x1az\xae>\x8a\x01v\f\xbcb;\xb3P\x00\x00\xe0\x94\xc7If\x80B\xe7\x11#\xa6H\x97^\b\xedc\x82\xf8>\x05\xe2\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xc7J9\x95\xf8\a\xde\x1d\xb0\x1a.\xb9\xc6.\x97\xd0T\x8fio\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7Pl\x10\x19\x12\x1f\xf0\x8a,\x8c\x15\x91\xa6^\xb4\xbd\xfbJ?\x89 \x86\xac5\x10R`\x00\x00\u07d4\xc7\\7\xce-\xa0k\xbc@\b\x11Y\u01ba\x0f\x97n9\x93\xb1\x89:y#\x15\x1e\xcfX\x00\x00\u07d4\xc7]\"Y0j\xec}\xf0\"v\x8ci\x89\x9ae!\x85\xdb\u0109\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7`\x97\x1b\xbc\x18\x1cj|\xf7tA\xf2BG\u045c\xe9\xb4\u03c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc7a0\xc7<\xb9!\x028\x02\\\x9d\xf9]\v\xe5J\xc6\u007f\xbe\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xc7e\xe0\x04v\x81\tG\x81j\xf1B\xd4m.\u7f28\xccO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc7g^VG\xb9\xd8\xda\xf4\xd3\xdf\xf1\xe5R\xf6\xb0qT\xac8\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xc7{\x01\xa6\xe9\x11\xfa\x98\x8d\x01\xa3\xab3dk\xee\xf9\xc18\xf3\x89'\x1bo\xa5\xdb\xe6\xcc\x00\x00\u07d4\u01c3z\u0420\xbf\x14\x18i7\xac\xe0lUF\xa3j\xa5OF\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01d8\x06\x03+\xc7\xd8(\xf1\x9a\u01a6@\u018e=\x82\x0f\xa4B\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u01d9\xe3N\x88\xff\x88\xbe}\xe2\x8e\x15\xe4\xf2\xa6=\v3\xc4\u02c9\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\u01ddPb\u01d6\xddwa\xf1\xf1>U\x8ds\xa5\x9f\x82\xf3\x8b\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\u01e0\x18\xf0\x96\x8aQ\xd1\xf6`<\\I\xdcT[\xcb\x0f\xf2\x93\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u01ef\xf9\x19)yt\x89UZ/\xf1\xd1M\\iZ\x10\x83U\x8965\u026d\xc5\u07a0\x00\x00\u0794\u01f1\xc8>c ?\x95G&>\xf6(.}\xa3;n\xd6Y\x88\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\u01f3\x9b\x06\x04Q\x00\f\xa1\x04\x9b\xa1T\xbc\xfa\x00\xff\x8a\xf2b\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\u01ff\x17\xc4\xc1\x1f\x98\x94\x1fP~w\bO\xff\xbd-\xbd=\xb5\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u01ff.\xd1\xed1)@\xeej\xde\xd1Qn&\x8eJ`HV\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xc7\xd4O\xe3,\u007f\x8c\xd5\xf1\xa9t'\xb6\xcd:\xfc\x9eE\x02>\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xc7\xd5\xc7\x05@\x81\xe9\x18\xech{Z\xb3n\x97=\x18\x13)5\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xc7\xde^\x8e\xaf\xb5\xf6+\x1a\n\xf2\x19\\\xf7\x93\u01c9L\x92h\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xc7\xe30\xcd\f\x89\n\u025f\xe7q\xfc\xc7\xe7\xb0\t\xb7A=\x8a\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc7\xea\xc3\x1a\xbc\xe6\xd5\xf1\u07a4\"\x02\xb6\xa6t\x15=\xb4z)\x89 \t\xc5\u023fo\xdc\x00\x00\xe0\x94\xc7\xecb\xb8\x04\xb1\xf6\x9b\x1e0p\xb5\xd3b\xc6/\xb3\t\xb0p\x8a\x02\xc4k\xf5A`f\x11\x00\x00\u07d4\xc7\xf7+\xb7X\x01k7G\x14\u0509\x9b\xce\"\xb4\xae\xc7\n1\x89:&\xc9G\x8f^-\x00\x00\u0794\xc8\v6\u047e\xaf\xba_\xccdM`\xacnF\xed)'\xe7\u0708\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xc8\x11\xc2\xe9\xaa\x1a\xc3F.\xba^\x88\xfc\xb5\x12\x0e\x9fn,\xa2\x89K\xe6\u0607\xbd\x87n\x00\x00\u07d4\xc8\x17\xdf\x1b\x91\xfa\xf3\x0f\xe3%\x15qr|\x97\x11\xb4]\x8f\x06\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xc8\x1f\xb7\xd2\x0f\u0480\x01\x92\xf0\xaa\xc1\x98\xd6\u05a3}?\xcb}\x89\x0e\x11I3\x1c-\xde\x00\x00\u07d4\xc8 \xc7\x11\xf0w\x05'8\a\xaa\xaam\xe4M\x0eKH\xbe.\x89\bg\x0e\x9e\xc6Y\x8c\x00\x00\u07d4\xc8#\x1b\xa5\xa4\x11\xa1>\"+)\xbf\xc1\b?v1X\xf2&\x8967\tlK\xcci\x00\x00\u07d4\xc86\xe2Jo\xcf)\x94;6\b\xe6b)\n!_e)\xea\x89\x0f\xd4Pd\xea\xee\x10\x00\x00\xe0\x94\xc8;\xa6\u0755I\xbe\x1d2\x87\xa5\xa6T\xd1\x06\xc3Lk]\xa2\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc8>\x9djX%;\uefb7\x93\xe6\xf2\x8b\x05JXI\x1bt\x89\x0fF\u00b6\xf5\xa9\x14\x00\x00\u07d4\xc8A\x88O\xa4x_\xb7s\xb2\x8e\x97\x15\xfa\xe9\x9aQ40]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8M\x9b\xea\n{\x9f\x14\x02 \xfd\x8b\x90\x97\u03ff\xd5\xed\xf5d\x89\x06\xab\x9e\u0091\xad}\x80\x00\u07d4\xc8RB\x8d+Xd\x97\xac\xd3\fV\xaa\x13\xfbU\x82\xf8D\x02\x893B\xd6\r\xff\x19`\x00\x00\u07d4\xc8S![\x9b\x9f-,\xd0t\x1eX^\x98{_\xb8\f!.\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xc8S%\uaca5\x9b>\xd8c\xc8j_)\x06\xa0B)\xff\xa9\x89\x19=\u007f}%=\xe0\x00\x00\u07d4\xc8^\xf2}\x82\x04\x03\x80_\xc9\xed%\x9f\xffd\xac\xb8\xd64j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc8akN\xc0\x91(\xcd\xff9\xd6\u4e6c\x86\xee\xc4q\xd5\xf2\x89\x01\r:\xa56\xe2\x94\x00\x00\xe0\x94\xc8a\x90\x90K\x8d\a\x9e\xc0\x10\xe4b\xcb\xff\xc9\b4\xff\xaa\\\x8a\x02#\x85\xa8'\xe8\x15P\x00\x00\u07d4\xc8q\r~\x8bZ;\u059aB\xfe\x0f\xa8\xb8|5\u007f\xdd\xcd\u0209\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc8sR\u06e5\x82\xee f\xb9\xc0\x02\xa9b\xe0\x03\x13Ox\xb1\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xc8|w\xe3\xc2J\xde\xcd\xcd\x108\xa3\x8bV\xe1\x8d\xea\u04f7\x02\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xc8}:\xe3\u0607\x04\u066b\x00\t\xdc\xc1\xa0\x06q1\xf8\xba<\x89j\xc5\xc6-\x94\x86\a\x00\x00\xe0\x94\u0201N4R>8\xe1\xf9'\xa7\xdc\xe8FjDz\t6\x03\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0202U\xed\xdc\xf5!\xc6\xf8\x1d\x97\xf5\xa4!\x81\xc9\a=N\xf1\x89\x0f\u00d0D\xd0\n*\x80\x00\u07d4\u0205\xa1\x8a\xab\xf4T\x1b{{~\xcd0\xf6\xfa\u619d\x95i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u020c\xa1\xe6\xe5\xf4\xd5X\xd17\x80\xf4\x88\xf1\rJ\xd3\x13\r4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u020e\xecT\xd3\x05\xc9(\xcc(H\xc2\xfe\xe251\xac\xb9mI\x89lj\u04c2\xd4\xfba\x00\x00\xe0\x94\u021c\xf5\x04\xb9\xf3\xf85\x18\x1f\xd8BO\\\xcb\xc8\xe1\xbd\xdf}\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0222\xc4\xe5\x9e\x1c\u007f\xc5H\x05X\x048\xae\xd3\xe4J\xfd\xf0\x0e\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\u022aI\u301f\b\x99\xf2\x8a\xb5~gCp\x9dXA\x903\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\u022b\x1a<\xf4l\xb8\xb0d\xdf.\"-9`s\x94 2w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0231\x85\x05%\xd9F\xf2\xae\x84\xf3\x17\xb1Q\x88\xc56\xa5\u0706\x89\x91\x8d\xdc:B\xa3\xd4\x00\x00\u07d4\xc8\xd4\xe1Y\x9d\x03\xb7\x98\t\xe0\x13\n\x8d\u00c4\b\xf0^\x8c\u04c9\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xc8\xdd'\xf1k\xf2$P\xf5w\x1b\x9f\xe4\xedO\xfc\xb3\t6\xf4\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xc8\xdezVL\u007f@\x12\xa6\xf6\xd1\x0f\u040fG\x89\x0f\xbf\a\u0509\x10CV\x1a\x88)0\x00\x00\u07d4\xc8\xe2\xad\xebT^I\x9d\x98,\f\x11sc\u03b4\x89\u0171\x1f\x895e\x9e\xf9?\x0f\xc4\x00\x00\xe0\x94\xc8\xe5X\xa3\xc5i~o\xb2:%\x94\u0200\xb7\xa1\xb6\x8f\x98`\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xc8\xf2\xb3 \xe6\xdf\xd7\t\x06\u0157\xba\xd2\xf9P\x13\x12\u01c2Y\x89Q\x93K\x8b:W\xd0\x00\x00\u07d4\xc9\x03\x00\xcb\x1d@w\xe6\xa6\xd7\xe1i\xa4`F\x8c\xf4\xa4\x92\u05c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\f7e\x15k\u028eH\x97\xab\x80$\x19\x15<\xbeR%\xa9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xc9\x10\xa9pUl\x97\x16\xeaS\xaff\xdd\xef\x93\x141$\x91=\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\xe0\x94\xc9\x12{\u007ff)\xee\x13\xfc?`\xbc/Dg\xa2\aE\xa7b\x8a\x03|\x9a\xa4\xe7\xceB\x1d\x80\x00\u07d4\xc9\x1b\xb5b\xe4+\xd4a0\xe2\u04eeFR\xb6\xa4\ub1bc\x0f\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\xe0\x94\xc90\x88y\x05m\xfe\x13\x8e\xf8 \x8fy\xa9\x15\u01bc~p\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xc94\xbe\xca\xf7\x1f\"_\x8bJK\xf7\xb1\x97\xf4\xac\x9604\\\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9?\xbd\xe8\xd4m+\xcc\x0f\xa9\xb3;\u063a\u007f\x80B\x12Ue\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xc9@\x89U:\xe4\xc2,\xa0\x9f\xbc\x98\xf5pu\xcf.\u0155\x04\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xc9A\x10\xe7\x1a\xfeW\x8a\xa2\x18\xe4\xfc(d\x03\xb03\n\u038d\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xc9F\u056c\xc14n\xba\nry\xa0\xac\x1dF\\\x99m\x82~\x8a\x03x=T_\xdf\n\xa4\x00\x00\u07d4\xc9J(\xfb20\xa9\xdd\xfa\x96Nw\x0f,\xe3\xc2S\xa7\xbeO\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xc9JXR\x03\xda{\xba\xfd\x93\xe1X\x84\xe6`\u0531\xea\xd8T\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xc9O|5\xc0'\xd4}\xf8\xefO\x9d\xf8Z\x92H\xa1}\xd2;\x89\x01\x9f\x8euY\x92L\x00\x00\u07d4\xc9Q\x90\f4\x1a\xbb\xb3\xba\xfb\xf7\xee )7pq\xdb\xc3j\x89\x11\xc2]\x00M\x01\xf8\x00\x00\u07d4\xc9S\xf94\xc0\xeb-\x0f\x14K\u06b0\x04\x83\xfd\x81\x94\x86\\\xe7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9f&r\x8a\xaaLO\xb3\xd3\x1c&\xdf:\xf3\x10\b\x17\x10\u0449\xb5\x0f\u03ef\xeb\xec\xb0\x00\x00\u07d4\xc9gQel\n\x8e\xf45{sD2!4\xb9\x83PJ\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\u0240Hh\u007f+\xfc\u027d\x90\xed\x18slW\xed\xd3R\xb6]\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0241\xd3\x12\u0487\xd5X\x87\x1e\u0757:\xbbv\xb9y\xe5\xc3^\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0242Xmc\xb0\xd7L \x1b\x1a\xf8A\x83r\xe3\fv\x16\xbe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u0249CO\x82Z\xaf\x9cU/h^\xba|\x11\xdbJ_\xc7:\x89\x1b(\u014d\x96\x96\xb4\x00\x00\u07d4\u0249\xee\xc3\a\u80db\x9dr7\xcf\xda\b\x82)b\xab\u41c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u0252\xbeY\xc6r\x1c\xafN\x02\x8f\x9e\x8f\x05\xc2\\UQ[\u0509\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0255{\xa9L\x1b)\xe5'~\xc3f\"pI\x04\xc6=\xc0#\x89h>\xfcg\x82d,\x00\x00\xe0\x94\u025a\x9c\xd6\xc9\xc1\xbe54\xee\u0352\xec\xc2/\\8\xe9Q[\x8a\x01\x05Y;:\x16\x9dw\x00\x00\xe0\x94\u026c\x01\xc3\xfb\t)\x03?\f\xcc~\x1a\xcf\uaae7\x94]G\x8a\x02\xa3j\x9e\x9c\xa4\xd2\x03\x80\x00\u07d4\u0276\x98\xe8\x98\xd2\rMO@\x8eNM\x06\x19\"\xaa\x85c\a\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u0276\xb6\x86\x11\x16\x91\xeej\xa1\x97\xc7#\x1a\x88\xdc`\xbd)]\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\u01ec\v\u0753B\xb5\xea\xd46\t#\xf6\x8cr\xa6\xbac:\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xc9\xc8\r\xc1.{\xab\x86\xe9I\xd0\x1eL>\xd3_+\x9b\xba_\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xc9\xd7dF\u056a\xdf\xf8\vh\xb9\x1b\b\u035b\xc8\xf5U\x1a\xc1\x89&\xb4\xbd\x91\x10\xdc\xe8\x00\x00\xe0\x94\xc9\u073b\x05oM\xb7\xd9\xda9\x93b\x02\u017d\x820\xb3\xb4w\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xc9\xe0&\b\x06h(\x84\x8a\xeb(\xc76r\xa1)%\x18\x1fM\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xca\x042\xcb\x15{Qy\xf0.\xbb\xa5\xc9\u0475O\xecM\x88\u028965\u026d\xc5\u07a0\x00\x00\u07d4\xca\x12,\xf0\U00094216\xb7HC\xf4\x9a\xfe\u043a\x16\x18\xee\u05c9\x1e[\x8f\xa8\xfe*\xc0\x00\x00\xe0\x94\xca\"\u0363`m\xa5\xca\xd0\x13\xb8\aG\x06\xd7\xe9\xe7!\xa5\f\x8a\x01q\x81\xc6\xfa9\x81\x94\x00\x00\u07d4\xca#\xf6-\xff\rd`\x03lb\xe8@\xae\xc5W~\v\xef\u0489\a\xa1\xfe\x16\x02w\x00\x00\x00\u07d4\xca%\xff4\x93L\x19B\xe2*N{\xd5o\x14\x02\x1a\x1a\xf0\x88\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xca7?\xe3\xc9\x06\xb8\xc6U\x9e\xe4\x9c\xcd\a\xf3|\xd4\xfbRf\x89a\t=|,m8\x00\x00\u07d4\xcaA\u032c0\x17 R\xd5\"\xcd//\x95}$\x81S@\x9f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xcaB\x88\x01N\xdd\xc5c/_\xac\xb5\xe3\x85\x17\xa8\xf8\xbc]\x98\x89\x12nr\xa6\x9aP\xd0\x00\x00\u07d4\xcaB\x88c\xa5\xca06\x98\x92\xd6\x12\x18>\xf9\xfb\x1a\x04\xbc\xea\x89Rf<\u02b1\xe1\xc0\x00\x00\u07d4\xcaI\xa5\xf5\x8a\xdb\xef\xae#\xeeY\xee\xa2A\xcf\x04\x82b.\xaa\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcaL\xa9\xe4w\x9dS\x0e\u02ec\xd4~j\x80X\xcf\xdee\u064f\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xcae~\xc0o\xe5\xbc\t\xcf#\xe5*\xf7\xf8\f\xc3h\x9en\u07890\xca\x02O\x98{\x90\x00\x00\u07d4\xcaf\xb2(\x0f\xa2\x82\u0176v1\xceU+b\xeeU\xad\x84t\x89j\xc4\"\xf54\x92\x88\x00\x00\xe0\x94\xcal\x81\x8b\xef\xd2Q6\x1e\x02t@h\xbe\x99\u062a`\xb8J\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcap\xf4\u077f\x06\x9d!C\xbdk\xbc\u007fikRx\x9b2\u7262\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xcatuvDjL\x8f0\xb0\x83@\xfe\xe1\x98\xdec\xec\x92\u03ca\x01|\x8e\x12\x06r*0\x00\x00\u07d4\xca{\xa3\xffSl~_\x0e\x158\x00\xbd8=\xb81)\x98\xe0\x89\t1\xac=k\xb2@\x00\x00\xe0\x94\u0282v\xc4w\xb4\xa0{\x80\x10{\x845\x94\x18\x96\a\xb5;\xec\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u0284\t\b>\x01\xb3\x97\xcf\x12\x92\x8a\x05\xb6\x84U\xceb\x01\u07c9V\xbcu\xe2\xd61\x00\x00\x00\u07d4\u0298\u01d8\x8e\xfa\b\xe9%\uf719ER\x03&\xe9\xf4;\x99\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u029a\x04*j\x80o\xfc\x92\x17\x95\x00\xd2D)\xe8\xabR\x81\x17\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\u029d\xec\x02\x84\x1a\xdf\\\xc9 WjQ\x87\xed\u04bdCJ\x18\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u029f\xaa\x17T/\xaf\xbb8\x8e\xab!\xbcL\x94\u89f3G\x88\x89lk\x8f\xce\r\x18y\x80\x00\xe0\x94\u02aah\xeel\xdf\r4EJv\x9b\r\xa1H\xa1\xfa\xaa\x18e\x8a\x01\x87.\x1d\xe7\xfeR\xc0\x00\x00\u07d4\u02ad\x9d\xc2\rX\x9c\xe4(\xd8\xfd\xa3\xa9\xd5:`{y\x88\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u02b0\xd3,\xf3v\u007f\xa6\xb3S|\x842\x8b\xaa\x9fPE\x816\x8a\x01\xe5\xb8\xfa\x8f\xe2\xac\x00\x00\x00\u07d4\u02b9\xa3\x01\xe6\xbdF\xe9@5P(\xec\xcd@\xceMZ\x1a\u00c9\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u02b9\xa9z\xda\x06\\\x87\x81nh`\xa8\xf1Bo\xe6\xb3\xd7u\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u02ba\xb6'N\xd1P\x89s~({\xe8x\xb7W\x93Hd\xe2\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\u02bd\xaf5OG \xa4f\xa7d\xa5(\xd6\x0e:H*9<\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xca\xcbg^\t\x96#T\x04\ufbfb.\u02c1R'\x1bU\xe0\x89%\xf2s\x93=\xb5p\x00\x00\u07d4\xca\xd1O\x9e\xbb\xa7f\x80\xeb\x83k\a\x9c\u007f{\xaa\xf4\x81\xedm\x89\f\xef={\xd7\xd04\x00\x00\xe0\x94\xca\xe3\xa2S\xbc\xb2\xcfN\x13\xba\x80\u0098\xab\x04\x02\xda|*\xa0\x8a\x01$\xbc\r\u0752\xe5`\x00\x00\u07d4\xca\xef\x02{\x1a\xb5\x04\xc7?A\xf2\xa1\ty\xb4t\xf9~0\x9f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xca\xf4H\x1d\x9d\xb7\x8d\xc4\xf2_{J\u023d;\x1c\xa0\x10k1\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xca\xfd\xe8U\x86L%\x98\xda<\xaf\xc0Z\u064d\U00089380H\x8a\x03\x00\xa8\xed\x96\xffJ\x94\x00\x00\xe0\x94\xcb\r\xd7\xcfN]\x86a\xf6\x02\x89C\xa4\xb9\xb7\\\x91D6\xa7\x8a\x19i6\x89t\xc0[\x00\x00\x00\u07d4\xcb\x1b\xb6\xf1\xda^\xb1\rH\x99\xf7\xe6\x1d\x06\xc1\xb0\x0f\u07f5-\x898E$\xccp\xb7x\x00\x00\u07d4\xcb=vl\x98?\x19+\xce\xca\xc7\x0fN\xe0=\xd9\xffqMQ\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcbB\xb4N\xb5\xfd`\xb5\x83~O\x9e\xb4rgR=\x1a\"\x9c\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xcbG\xbd0\u03e8\xecTh\xaa\xa6\xa9FB\xce\xd9\xc8\x19\xc8\u0509\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcbH\xfe\x82e\u066fU\xebp\x06\xbc3VE\xb0\xa3\xa1\x83\xbe\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xcbJ\x91M+\xb0)\xf3._\xef\\#LO\xec--\xd5w\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcbJ\xbf\u0082\xae\xd7n]W\xaf\xfd\xa5B\xc1\xf3\x82\xfc\xac\xf4\x8a\x01\xb9\x0f\x11\xc3\x18?\xaa\x00\x00\u07d4\xcbJ\xd0\xc7#\xdaF\xabV\xd5&\xda\f\x1d%\xc7=\xaf\xf1\n\x89\x1b\xa5\xab\xf9\xe7y8\x00\x00\u07d4\xcbK\xb1\xc6#\xba(\xdcB\xbd\xaa\xa6\xe7N\x1d*\xa1%l*\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xcbPXt\x12\x82#\x04\xeb\u02e0}\xab:\x0f\t\xff\xfe\u4189JD\x91\xbdm\xcd(\x00\x00\u07d4\xcbX\x99\v\u0350\u03ffm\x8f\t\x86\xf6\xfa`\x02v\xb9N-\x8964\xbf9\xab\x98x\x80\x00\u07d4\xcbh\xaeZ\xbe\x02\xdc\xf8\xcb\u016aq\x9c%\x81FQ\xaf\x8b\x85\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xcbty\x10\x9bC\xb2fW\xf4F_M\x18\xc6\xf9t\xbe_B\x89b\xa9\x92\xe5:\n\xf0\x00\x00\xe0\x94\xcb}+\x80\x89\xe91,\u026e\xaa's\xf3S\b\xecl*{\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u02c6\xed\xbc\x8b\xbb\x1f\x911\x02+\xe6IV^\xbd\xb0\x9e2\xa1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u02d3\x19\x9b\x9c\x90\xbcI\x15\xbd\x85\x9e=B\x86m\xc8\xc1\x87I\x89\f\x90\xdf\a\xde\xf7\x8c\x00\x00\u07d4\u02d4\xe7o\xeb\xe2\b\x11g3\xe7n\x80]H\xd1\x12\xec\x9f\u028965\u026d\xc5\u07a0\x00\x00\u07d4\u02dbQ\x03\xe4\u0389\xafOd\x91aP\xbf\xf9\xee\u02df\xaa\\\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\\zP<\xc8\xe0\xd0Iq\xca\x05\xc7b\xf9\xb7b\xb4\x8b\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u02e2\x88\xcd<\x1e\xb4\u055d\xdb\x06\xa6B\x1c\x14\xc3E\xa4{$\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u02f3\x18\x9eK\xd7\xf4_\x17\x8b\x1c0\xc7n&1MJK\n\x89\x0f\xfe\vg|e\xa9\x80\x00\xe0\x94\u02f7\xbe\x17\x95?,\u0313\u1f19\x80[\xf4U\x11CNL\x8a\n\xae[\x9d\xf5m/ \x00\x00\xe0\x94\xcb\xc0KM\x8b\x82\xca\xf6p\x99o\x16\f6)@\xd6o\xcf\x1a\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcb\u07974\xb8\xe6\xaaS\x8c)\x1dm\u007f\xac\xed\xb0\xf38\xf8W\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcb\xe1\xb9H\x86M\x84t\xe7e\x14XX\xfc\xa4U\x0fxK\x92\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xe5/\xc53\xd7\xdd`\x8c\x92\xa2`\xb3|?E\u07b4\xeb3\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcb\xe8\x10\xfe\x0f\xec\xc9dGJ\x1d\xb9w(\xbc\x87\xe9s\xfc\xbd\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xf1j\x0f\xe2tRX\xcdR\xdb+\xf2\x19T\xc9u\xfcj\x15\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\xcb\xf3\u007f\xf8T\xa2\xf1\xceS\x93D\x94wx\x92\xd3\xeceW\x82\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcb\xfaj\xf6\u0083\xb0F\xe2w,`c\xb0\xb2\x15S\xc4\x01\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcb\xfav\xdb\x04\xce8\xfb ]7\xb8\xd3w\xcf\x13\x80\xda\x03\x17\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xcc\x03I\x85\xd3\xf2\x8c-9\xb1\xa3K\xce\xd4\u04f2\xb6\xca#N\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xcc\x04<C\x88\xd3E\xf8\x84\u0185^q\x14*\x9fA\xfdi5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xcc\x1dn\xad\x01\xaa\xda>\x8d\u01f9]\xca%\xdf&\xee\xfac\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc+_D\x8f5(\xd3\xfeA\xcc}\x1f\xa9\xc0\xdcv\xf1\xb7v\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc-\x04\xf0\xa4\x01q\x89\xb3@\xcaw\x19\x86A\xdc\xf6Ek\x91\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xccA\x9f\u0651+\x85\x13VY\xe7z\x93\xbc=\xf1\x82\xd4Q\x15\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccE\xfb:U[\xad\x80{8\x8a\x03W\xc8U _|u\xe8\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xccHAM*\xc4\xd4*Yb\xf2\x9e\xeeD\x97\t/C\x13R\x89\b\xbaR\xe6\xfcE\xe4\x00\x00\u07d4\xccJ/,\xf8l\xf3\xe43u\xf3`\xa4sF\x91\x19_\x14\x90\x89I\x15\x05;\xd1)\t\x80\x00\u07d4\xccO\x0f\xf2\xae\xb6}T\xce;\xc8\xc6Q\v\x9a\xe8>\x9d2\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xccO\xaa\xc0\v\xe6b\x8f\x92\xefk\x8c\xb1\xb1\xe7j\xac\x81\xfa\x18\x89\v\"\xa2\xea\xb0\xf0\xfd\x00\x00\xe0\x94\xccO\xebr\u07d8\xff5\xa18\xe0\x17a\xd1 ?\x9b~\xdf\n\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xcc`oQ\x13\x97\xa3\x8f\u01c7+\u04f0\xbd\x03\xc7\x1b\xbdv\x8b\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcc`\xf86\xac\xde\xf3T\x8a\x1f\xef\u0321>\u01a97\xdbD\xa0\x89\x04\xb0m\xbb\xb4\x0fJ\x00\x00\u07d4\xccl\x03\xbd`>\t\xdeT\xe9\xc4\u056cmA\xcb\xceqW$\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xccl-\xf0\x0e\x86\xec\xa4\x0f!\xff\xda\x1ag\xa1i\x0fG|e\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\xccm{\x12\x06\x1b\xc9m\x10M`me\xff\xa3+\x006\xeb\a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xccs\xdd5kIy\xb5y\xb4\x01\xd4\xccz1\xa2h\xdd\xceZ\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xccu\x8d\a\x1d%\xa62\n\xf6\x8c]\xc9\xc4\xf6\x95[\xa9E \x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcc{\x04\x81\xcc2\xe6\xfa\xef#\x86\xa0p\"\xbc\xb6\xd2\u00f4\xfc\x89\xabM\xcf9\x9a:`\x00\x00\xe0\x94\u0314;\xe1\",\xd1@\n#\x99\xdd\x1bE\x94E\xcfmT\xa9\x8a\x02\xa7@\xaee6\xfc\x88\x00\x00\u07d4\u0315\x19\xd1\xf3\x98_k%^\xad\xed\x12\xd5bJ\x97'!\xe1\x8965\u026d\xc5\u07a0\x00\x00\u0794\u031a\xc7\x15\xcdo&\x10\xc5+XgdV\x88B\x97\x01\x8b)\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\u0320{\xb7\x94W\x1dJ\xcf\x04\x1d\xad\x87\xf0\xd1\xef1\x85\xb3\x19\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u032b\xc6\x04\x8aSFD$\xfc\xf7n\xeb\x9en\x18\x01\xfa#\u0509\x02\xab{&\x0f\xf3\xfd\x00\x00\u07d4\u032e\r=\x85*}\xa3\x86\x0f\x066\x15L\nl\xa3\x16(\u0509\x05\xc6\xd1+k\xc1\xa0\x00\x00\u07d4\xcc\xca$\xd8\xc5mn,\a\xdb\bn\xc0~X[\xe2g\xac\x8d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcc\xd5!\x13-\x98l\xb9hi\x84&\"\xa7\u0762l>\xd0W\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcc\xf49u\xb7k\xfes_\xec<\xb7\xd4\xdd$\xf8\x05\xba\tb\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xcc\xf6*f?\x13S\xba.\xf8\xe6R\x1d\xc1\xec\xb6s\xec\x8e\xf7\x89\b=lz\xabc`\x00\x00\u07d4\xcc\xf7\x11\r\x1b\u0667K\xfd\x1d}}-\x9dU`~{\x83}\x890\xca\x02O\x98{\x90\x00\x00\u07d4\xcc\xfdrW`\xa6\x88#\xff\x1e\x06/L\xc9~\x13`\xe8\u0657\x89\x15\xacV\xed\xc4\xd1,\x00\x00\u07d4\xcd\x02\x0f\x8e\xdf\xcfRG\x98\xa9\xb7:d\x034\xbb\xf7/\x80\xa5\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xcd\x06\xf8\xc1\xb5\u037d(\xe2\xd9kcF\xc3\xe8Z\x04\x83\xba$\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcd\a.n\x183\x13y\x95\x19m{\xb1r_\xef\x87a\xf6U\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xcd\n\x16\x1b\xc3g\xae\t'\xa9*\xac\x9c\xf6\xe5\bg\x14\xef\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\n\xf3GN\"\xf0i\xec4\a\x87\r\xd7pD=[\x12\xb0\x89\x8e^\xb4\xeew\xb2\xef\x00\x00\u07d4\xcd\v\x02W\u70e3\xd2\xc2\u3e9dny\xb7^\xf9\x80$\u0509\x9f\xad\x06$\x12y\x16\x00\x00\u07d4\xcd\x10,\xd6\xdb=\xf1J\u05af\x0f\x87\xc7$y\x86\x1b\xfc=$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcd\x1ef\xedS\x9d\xd9/\xc4\v\xba\xa1\xfa\x16\u078c\x02\xc1ME\x89\fw\xe4%hc\xd8\x00\x00\u07d4\xcd\x1e\xd2c\xfb\xf6\xf6\xf7\xb4\x8a\xef\x8fs=2\x9dC\x82\xc7\u01c9\x01\x00\xbd3\xfb\x98\xba\x00\x00\u07d4\xcd*6\xd7S\xe9\xe0\xed\x01*XMqh\aX{A\xd5j\x89\x0e+\xa7[\v\x1f\x1c\x00\x00\u07d4\xcd2\xa4\xa8\xa2\u007f\x1c\xc69T\xaacOxW\x05s4\u01e3\x89:\xd1fWlr\xd4\x00\x00\u07d4\xcd5\xff\x01\x0e\xc5\x01\xa7!\xa1\xb2\xf0z\x9c\xa5\x87}\xfc\xf9Z\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xcdC\x06\xd7\xf6\x94z\xc1tMN\x13\xb8\xef2\xcbe~\x1c\x00\x89\x1b\x1a\xb3\x19\xf5\xecu\x00\x00\u07d4\xcdC%\x8bs\x92\xa90\x83\x9aQ\xb2\xef\x8a\xd24\x12\xf7Z\x9f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcdI\xbf\x18^p\xd0E\a\x99\x9f\x92\xa4\xdeDU1('\u040965\u026d\xc5\u07a0\x00\x00\u07d4\xcdU\x10\xa2B\u07f0\x18=\xe9%\xfb\xa8f\xe3\x12\xfa\xbc\x16W\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xcdVj\u05f8\x83\xf0\x1f\u04d9\x8a\x9aX\xa9\xde\xe4rM\u0725\x89\x030\xae\x185\xbe0\x00\x00\xe0\x94\xcdY\xf3\xdd\xe7~\t\x94\v\xef\xb6\xeeX\x03\x19e\xca\xe7\xa36\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcdr]p\xbe\x97\xe6w\xe3\xc8\xe8\\\v&\xef1\xe9\x95PE\x89Hz\x9a0E9D\x00\x00\xe0\x94\xcd~G\x90\x94d\xd8q\xb9\xa6\xdcv\xa8\xe9\x19]\xb3H^z\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xcd~\xce\bkKa\x9b;6\x93R\xee8\xb7\x1d\xdb\x06C\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xcd\u007f\t\xd7\xedf\xd0\u00cb\u016dN2\xb7\xf2\xb0\x8d\xc1\xb3\r\x89>;\xb3M\xa2\xa4p\x00\x00\u07d4\u0355)I+\\)\xe4u\xac\xb9A@+=;\xa5\x06\x86\xb0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0355\xfaB=o\xc1 'J\xac\xde\x19\xf4\xee\xb7f\xf1\x04 \x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u035bL\xefs9\f\x83\xa8\xfdq\u05f5@\xa7\xf9\u03cb\x8c\x92\x89\x04\xe1\x00;(\xd9(\x00\x00\u07d4\u0361t\x11\t\xc0&[?\xb2\xbf\x8d^\xc9\u00b8\xa34kc\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0361\xb8\x86\u39d5\u027aw\x91N\n/\xe5go\x0f\\\u03c9\x05\xbf`\xeaB\xc2\x04\x00\x00\u07d4\u0364S\x0fK\x9b\xc5\t\x05\xb7\x9d\x17\u008f\xc4o\x954\x9b\u07c93\x10\xe0I\x11\xf1\xf8\x00\x00\u07d4\u036bF\xa5\x90 \x80do\xbf\x95B\x04 J\xe8\x84\x04\x82+\x89\x1d\x8a\x96\xe5\xc6\x06\xeb\x00\x00\u07d4\u0375\x97)\x900\x18?n-#\x853\xf4d*\xa5\x87T\xb6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xcd\xd5\u0601\xa76,\x90p\a;\u07fcu\xe7$S\xacQ\x0e\x89-\xa5\x18\xea\xe4\x8e\xe8\x00\x00\u07d4\xcd\xd6\rs\xef\xaa\xd8s\u027b\xfb\x17\x8c\xa1\xb7\x10Z\x81\xa6\x81\x89\x01\xbc\x16\xd6t\xec\x80\x00\x00\u07d4\xcd\xd9\xef\xacMm`\xbdq\xd9U\x85\xdc\xe5\u0557\x05\xc15d\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xcd\xe3m\x81\xd1(\u015d\xa1Ee!\x93\xee\u00bf\xd9e\x86\xef\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcd\xea8o\x9d\x0f\xd8\x04\xd0(\x18\xf27\xb7\xd9\xfavF\xd3^\x89\xa3I\xd3m\x80\xecW\x80\x00\u07d4\xcd\xec\xf5gT3\u0370\xc2\xe5Zh\xdb]\x8b\xbexA\x9d\u0489\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xcd\xfd\x82\x173\x97%\xd7\xeb\xac\x11\xa66U\xf2e\xef\xf1\xcc=\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce\a\x9fQ\x88wt\xd8\x02\x1c\xb3\xb5u\xf5\x8f\x18\xe9\xac\xf9\x84\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xce\x18\x84\u077b\xb8\xe1\x0eM\xbanD\xfe\xee\u00a7\xe5\xf9/\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\x1b\f\xb4j\xae\xcf\u05db\x88\f\xad\x0f-\u068a\x8d\xed\u0431\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xce&\xf9\xa50_\x83\x81\tCT\xdb\xfc\x92fN\x84\xf9\x02\xb5\x89\fz\xaa\xb0Y\x1e\xec\x00\x00\u07d4\xce-\xea\xb5\x1c\n\x9a\xe0\x9c\xd2\x12\xc4\xfaL\xc5+S\xcc\r\xec\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xce.\r\xa8\x93F\x99\xbb\x1aU>U\xa0\xb8\\\x16\x945\xbe\xa3\x8a\x01\x0f\fid\x10\xe3\xa9\x00\x00\u07d4\xce:a\xf0F\x1b\x00\x93^\x85\xfa\x1e\xad\x82\xc4^Zd\u0508\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceK\x06]\xbc\xb20G 2b\xfbH\xc1\x18\x83d\x97tp\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xceS\xc8\xcd\xd7B\x96\xac\xa9\x87\xb2\xbc\x19\u00b8u\xa4\x87I\u0409\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xce^\x04\xf0\x18Ci\xbc\xfa\x06\xac\xa6o\xfa\x91\xbfY\xfa\x0f\xb9\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xce^\xb6:{\xf4\xfb\xc2\xf6\u4ea0\u018a\xb1\xcbL\xf9\x8f\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xceb\x12Z\xde\xc37\n\xc5!\x10\x95:Nv\v\xe9E\x1e;\x89\b=lz\xabc`\x00\x00\xe0\x94\xceq\bmL`%T\xb8-\xcb\xfc\xe8\x8d cMS\xccM\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4\u038akmP3\xb1I\x8b\x1f\xfe\xb4\x1aAU\x04\x05\xfa\x03\xa2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0397\x86\xd3q/\xa2\x00\xe9\xf6\x857\xee\xaa\x1a\x06\xa6\xf4ZK\x89a\t=|,m8\x00\x00\u07d4\u039d!\u0192\xcd<\x01\xf2\x01\x1fP_\x87\x006\xfa\x8fl\u0489\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u03a2\x89f#\xf4\x91\x02\x87\xa2\xbd\u017e\x83\xae\xa3\xf2\xe6\xde\b\x8a\x01\xfbZ7Q\xe4\x90\xdc\x00\x00\u07d4\u03a3JM\xd9=\u066e\xfd9\x90\x02\xa9}\x99z\x1bK\x89\u0349QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\u03a4?pu\x81k`\xbb\xfc\u62d9:\xf0\x88\x12p\xf6\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\u03a8t3AS<\xb2\xf0\xb9\xc6\xef\xb8\xfd\xa8\rw\x16(%\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u03b0\x89\xec\x8ax3~\x8e\xf8\x8d\xe1\x1bI\xe3\u0751\x0ft\x8f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u03b3=x\xe7Tz\x9d\xa2\xe8}Q\xae\xc5\xf3D\x1c\x87\x92:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u03b3\x898\x1dH\xa8\xaeO\xfcH:\u043b^ L\xfd\xb1\xec\x89('\xe6\xe4\xddb\xba\x80\x00\u07d4\xce\xc6\xfce\x85?\x9c\xce_\x8e\x84Fv6.\x15y\x01_\x02\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xce\xd3\u01fe\x8d\xe7XQ@\x95*\xebP\x1d\xc1\xf8v\ucbf0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xce\xd8\x1e\xc3S?\xf1\xbf\xeb\xf3\xe3\x84>\xe7@\xad\x11u\x8d>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xce\u0733\xa1\u0584?\xb6\xbe\xf6Ca}\xea\U000cf398\xdd_\x89\x19\xe2\xa4\xc8\x18\xb9\x06\x00\x00\u07d4\xce\xe6\x99\xc0pzx6%+)/\x04|\xe8\xad(\x9b/U\x89\x11\x9a\x1e!\xaaiV\x00\x00\u07d4\xce\xedG\xca[\x89\x9f\xd1b?!\xe9\xbdM\xb6Z\x10\u5c1d\x89\a8w@L\x1e\xee\x00\x00\u07d4\xce\xf7tQ\u07e2\xc6C\xe0\v\x15mlo\xf8N#s\xebf\x89\n1\x06+\xee\xedp\x00\x00\u07d4\xcf\x11i\x04\x1c\x17E\xe4[\x17$5\xa2\xfc\x99\xb4\x9a\xce+\x00\x89\x01\xbb\x88\xba\xab-|\x00\x00\xe0\x94\xcf\x15v\x12vN\x0f\u0596\xc8\xcb_\xba\x85\xdfL\r\xdc<\xb0\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u0794\xcf\x1b\xdby\x9b.\xa6<\xe14f\x8b\xdc\x19\x8bT\x84\x0f\x18\v\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xcf\"\x88\xefN\xbf\x88\xe8m\xb1=\x8a\x0e\v\xf5*\x05e\x82\u00c9\x89Po\xbf\x97@t\x00\x00\u07d4\xcf&Ni%\x13\t\x06\xc4\xd7\xc1\x85\x91\xaaA\xb2\xa6\u007foX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf&\xb4{\xd04\xbcP\x8elK\xcf\xd6\xc7\xd3\x004\x92Wa\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xcf.*\xd65\xe9\x86\x1a\xe9\\\xb9\xba\xfc\xca\x03kR\x81\xf5\u038a\at2!~h6\x00\x00\x00\u07d4\xcf.s@B\xa3U\xd0_\xfb.9\x15\xb1h\x11\xf4Zi^\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf4\x8f/\xe4{~A<\az{\xaf:u\xfb\xf8B\x86\x92\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xcf?\x91(\xb0r\x03\xa3\xe1\r}WU\xc0\u012b\xc6\xe2\xca\u008a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xcf?\xbf\xa1\xfd2\u05e6\xe0\xe6\xf8\xefN\xabW\xbe4\x02\\L\x899\xa1\xc0\xf7YMH\x00\x00\u07d4\xcfAftn\x1d;\xc1\xf8\xd0qK\x01\xf1~\x8ab\xdf\x14d\x896w\x03n\xdf\n\xf6\x00\x00\u07d4\xcfO\x118\xf1\xbdk\xf5\xb6\u0505\xcc\xe4\xc1\x01\u007f\u02c5\xf0}\x89/\u043cw\xc3+\xff\x00\x00\u07d4\xcfZo\x9d\xf7Uy\xc6D\xf7\x94q\x12\x15\xb3\rw\xa0\xce@\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcf^\x0e\xac\u0473\x9d\x06U\xf2\xf7u5\xeff\b\xeb\x95\v\xa0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xcfhM\xfb\x83\x04r\x93U\xb5\x83\x15\xe8\x01\x9b\x1a\xa2\xad\x1b\xac\x89\x17r$\xaa\x84Lr\x00\x00\u07d4\xcfi@\x81\xc7m\x18\xc6L\xa7\x13\x82\xbe\\\xd6;<\xb4v\xf8\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xcfnR\xe6\xb7t\x80\xb1\x86~\xfe\xc6Dm\x9f\xc3\xcc5w\xe8\x89\f\t\x01\xf6\xbd\x98y\x00\x00\u07d4\u03c8: 2\x96g\xea\"j\x1e<v]\xbbk\xab2!\x9f\x89\xa4\xbe5d\xd6\x16f\x00\x00\xe0\x94\u03c8\x825\x9c\x0f\xb23\x87\xf5g@t\u0631z\xdeQ/\x98\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\u03c9\xf7F\v\xa3\xdf\xe8<Z\x1d:\x01\x9e\xe1%\x0f$/\x0f\x895h\x13\xcd\xce\xfd\x02\x80\x00\u07d4\u03d2:]\x8f\xbc=\x01\xaa\a\x9d\x1c\xfeKC\xce\a\x1b\x16\x11\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u03db\u9e6b\x86\xc6kY\x96\x8eg\xb8\xd4\xdc\xffF\xb1\x81J\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\u03e8\xb3q'\x14\x9b\xdb\xfe\xe2\\4\xd8xQ\tQ\xea\x10\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u03ec.\x1b\xf32\x05\xb0U3i\x1a\x02&~\xe1\x9c\xd8\x186\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u03fb2\xb7\xd0$5\x0e3!\xfa \u0269\x14\x03Sr\xff\u0189\x15\xbeat\xe1\x91.\x00\x00\u07d4\xcf\xc4\xe6\xf7\xf8\xb0\x11AK\xfb\xa4/#\xad\xfa\xa7\x8dN\xcc^\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xcf\xd2r\x8d\xfb\x8b\xdb\xf3\xbfsY\x8an\x13\xea\xf40R\xea+\x89\t79SM(h\x00\x00\u07d4\xcf\xd4t\x93\xc9\xf8\x9f\u603d\xa5uM\xd7\xc9\xcf\xe7\xcb[\xbe\x89\x02\xf4sQ4H\xfe\x00\x00\u07d4\xcf\xde\x0f\xc7]o\x16\xc4C\xc3\x03\x82\x177-\x99\xf5\xd9\a\xf7\x89\x83\"^c\x96\xb5\xec\x00\x00\xe0\x94\xcf\xe2\u02af<\xec\x97\x06\x1d\t9t\x879\xbf\xfehJ\xe9\x1f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xcf\xea\u02ae\xd5r\x85\xe0\xacrh\xcejN5\xec\xfd\xb2B\u05c9:\xe4\xd4$\x01\x90`\x00\x00\u07d4\xcf\ucfa0|'\x00/e\xfeSK\xb8\x84-\t%\u01c4\x02\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xcf\xee\x05\u019d\x1f)\xe7qF\x84\u020d\xe5\xa1`\x98\xe9\x13\x99\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xcf\xf6\xa6\xfe>\x9a\x92*\x12\xf2\x1f\xaa\x03\x81V\x91\x8cO\u02dc\x89\x04E\x91\xd6\u007f\xec\xc8\x00\x00\u07d4\xcf\xf7\xf8\x9aMB\x19\xa3\x82\x95%\x131V\x82\x10\xff\xc1\xc14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xcf\xf8\xd0k\x00\xe3\xf5\f\x19\x10\x99\xadV\xbaj\xe2eq\u0348\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xcf\xfcI\xc1x~\ubcb5l\xab\xe9$\x04\xb66\x14}EX\x8a\x013\xe00\x8f@\xa3\u0680\x00\u07d4\xd0\bQ;'`J\x89\xba\x17c\xb6\xf8L\u6233F\x94[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x0f\x06r\x86\xc0\xfb\u0402\xf9\xf4\xa6\x10\x83\xecv\u07b3\xce\xe6\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\x15\xf6\xfc\xb8M\xf7\xbbA\x0e\x8c\x8f\x04\x89J\x88\x1d\xca\xc27\x898E$\xccp\xb7x\x00\x00\u07d4\xd0\x1a\xf9\x13O\xafRW\x17N\x8by\x18oB\xee5Nd-\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0!\b\u04ae<\xab\x10\xcb\xcf\x16W\xaf\">\x02|\x82\x10\xf6\x89lm\x84\xbc\xcd\xd9\xce\x00\x00\u07d4\xd0*\xfe\u03ce.\u00b6*\u022d Aa\xfd\x1f\xaew\x1d\x0e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd01\x919\xfb\xab.\x8e*\xcc\xc1\xd9$\u0531\x1d\xf6ilZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd07\xd2\x15\xd1\x1d\x1d\xf3\xd5O\xbd2\x1c\u0495\xc5F^';\x89K\xe4\xe7&{j\xe0\x00\x00\u07d4\xd0:-\xa4\x1e\x86\x8e\xd3\xfe\xf5t[\x96\xf5\xec\xa4b\xffo\u0689\xa2\xa1]\tQ\x9b\xe0\x00\x00\xe0\x94\xd0?\xc1eWj\xae\xd5%\xe5P,\x8e\x14\x0f\x8b.\x86\x969\x8a\x01sV\u0633%\x01\xc8\x00\x00\u07d4\xd0C\xa0\x11\xecBp\xee~\u0239hsu\x15\xe5\x03\xf80(\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd0K\x86\x1b=\x9a\xccV:\x90\x16\x89\x94\x1a\xb1\xe1\x86\x11a\xa2\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd0ZD|\x91\x1d\xbb'[\xfb.Z7\xe5\xa7\x03\xa5o\x99\x97\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd0_\xfb+t\xf8g O\xe51e;\x02H\xe2\x1c\x13TN\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0bX\x81q\u03d9\xbb\xebX\xf1&\xb8p\xf9\xa3r\x8da\xec\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\u07d4\xd0c\x8e\xa5q\x89\xa6\xa6\x99\x02J\u05ccq\xd99\xc1\xc2\xff\x8c\x89\x8e\xaeVg\x10\xfc \x00\x00\xe0\x94\xd0d\x8aX\x1b5\b\xe15\xa2\x93]\x12\xc9epE\xd8q\u028a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xd0q\x19)f\xebi\xc3R\x0f\xca:\xa4\xdd\x04)~\xa0KN\x89\x05\xf6\x8e\x811\xec\xf8\x00\x00\u07d4\xd0q\x85 \xea\xe0\xa4\xd6-p\xde\x1b\xe0\xcaC\x1c^\xea$\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd0w]\xba*\xf4\xc3\n:x6Y9\xcdq\xc2\xf9\u0795\u0489i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd0{\xe0\xf9\t\x97\xca\xf9\x03\u022c\x1dS\xcd\xe9\x04\xfb\x19\aA\x8968\x908\xb6\x99\xb4\x00\x00\u07d4\xd0~Q\x18d\xb1\u03d9i\xe3V\x06\x02\x82\x9e2\xfcNq\xf5\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u0400\x94\x98\xc5H\x04z\x1e**\xa6\xa2\x9c\xd6\x1a\x0e\xe2h\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0402'_tZ,\xac\x02v\xfb\xdb\x02\u0532\xa3\xab\x17\x11\xfe\x89\x01\xa0Ui\r\x9d\xb8\x00\x00\u07d4\u040f\xc0\x9a\x000\xfd\t(\xcd2\x11\x98X\x01\x82\xa7j\xae\x9f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0413\xe8)\x81\x9f\xd2\xe2[\x978\x00\xbb=XA\xdd\x15-\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0414J\xa1\x85\xa13pa\xae \u071d\xd9l\x83\xb2\xbaF\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u0416V[|t\a\xd0e6X\x03U\xfd\xd6\xd29\x14J\xa1\x89\r\x8drkqw\xa8\x00\x00\u07d4\u041c\xb2\xe6\b-i:\x13\xe8\xd2\xf6\x8d\xd1\u0744a\xf5X@\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u0426\xc6\xf9\xe9\u0133\x83\xd7\x16\xb3\x1d\xe7\x8dVAM\xe8\xfa\x91\x89\x10CV\x1a\x88)0\x00\x00\u07d4\u0427 \x9b\x80\xcf`\xdbb\xf5}\n]}R\x1ai`fU\x89\b\xacr0H\x9e\x80\x00\x00\xe0\x94\u0428\xab\xd8\n\x19\x9bT\xb0\x8be\xf0\x1d \x9c'\xfe\xf0\x11[\x8a\x01a\xc6&\xdca\xa2\xef\x80\x00\xe0\x94\u042b\xccp\xc0B\x0e\x0e\x17/\x97\xd4;\x87\xd5\xe8\f3n\xa9\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u042es]\x91^\x94hf\xe1\xfe\xa7~^\xa4f\xb5\xca\xdd\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0431\x1do+\u0394^\fjP \u00f5'S\xf8\x03\xf9\u0449\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xd0\xc1\x01\xfd\x1f\x01\xc6?k\x1d\x19\xbc\x92\r\x9f\x93#\x14\xb16\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd0\xc5Z\xbf\x97o\xdc=\xb2\xaf\u9f99\u0519HMWl\x02\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd0\u0422\xadE\xf5\x9a\x9d\xcc\u0195\xd8_%\xcaF\xed1\xa5\xa3\x89-\x89W}}@ \x00\x00\u07d4\xd0\xd6,G\xea`\xfb\x90\xa3c\x92\t\xbb\xfd\xd4\xd93\x99\x1c\u0189\n\x84Jt$\xd9\xc8\x00\x00\u07d4\xd0\xdbEax o\\D0\xfe\x00Pc\x90<=zI\xa7\x89&I\x1eE\xa7S\xc0\x80\x00\u07d4\xd0\xe1\x94\xf3K\x1d\xb6\t(\x85\t\xcc\xd2\xe7;a1\xa2S\x8b\x8965f3\xeb\xd8\xea\x00\x00\u07d4\xd0\xe3^\x04vF\xe7Y\xf4Qp\x93\xd6@\x86BQ\u007f\bM\x89\u054f\xa4h\x18\xec\u02c0\x00\u07d4\xd0\xeeM\x02\xcf$8,0\x90\xd3\xe9\x95`\xde6xs\\\u07c9\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\xd0\xf0OR\x10\x9a\xeb\xec\x9a{\x1e\x932v\x1e\x9f\xe2\xb9{\xb5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd0\xf9Yx\x11\xb0\xb9\x92\xbb}7W\xaa%\xb4\xc2V\x1d2\xe2\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\x03\x02\xfa\xa1\x92\x9a2i\x04\xd3v\xbf\v\x8d\xc9:\xd0LL\x89a\t=|,m8\x00\x00\xe0\x94\xd1\x10\r\xd0\x0f\xe2\xdd\xf1\x81c\xad\x96M\vi\xf1\xf2\xe9e\x8a\x8a\x01C\x12\tU\xb2Pk\x00\x00\u07d4\xd1\x16\xf3\xdc\xd5\xdbtK\xd0\b\x88v\x87\xaa\x0e\xc9\xfdr\x92\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\x19A|Fs,\xf3M\x1a\x1a\xfby\xc3\xe7\xe2\u034e\xec\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1-w\xae\x01\xa9-5\x11{\xacpZ\xac\u0642\xd0.t\xc1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd15yK\x14\x9a\x18\xe1G\xd1nb\x1ai1\xf0\xa4\n\x96\x9a\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xd1C%8\xe3[vd\x95j\u4563*\xbd\xf0A\xa7\xa2\x1c\x8a\x04+\xf0kx\xed;P\x00\x00\u07d4\xd1C\x82g#\x17\x04\xfcr\x80\xd5c\xad\xf4v8D\xa8\a\"\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd1S\x8e\x9a\x87\u5729\xec\x8eX&\xa5\xb7\x93\xf9\x9f\x96\xc4\u00c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd1d\x85\x03\xb1\xcc\u0178\xbe\x03\xfa\x1e\xc4\xf3\xee&~j\xdf{\x8a\x01;\xef\xbfQ\xee\xc0\x90\x00\x00\xe0\x94\xd1h,!Y\x01\x8d\xc3\xd0\u007f\b$\n\x8c`m\xafe\xf8\xe1\x8a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xd1q\xc3\xf2%\x8a\xef5\xe5\x99\xc7\xda\x1a\xa0s\x00#M\xa9\xa6\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1w\x8c\x13\xfb\xd9h\xbc\b<\xb7\xd1\x02O\xfe\x1fI\xd0,\xaa\x89\xd9\xec\xb4\xfd \x8eP\x00\x00\u07d4\xd1\u007f\xbe\"\xd9\x04b\xed7(\x06p\xa2\xea\v0\x86\xa0\xd6\u0589\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\u0441\x1cU\x97i\x80\xf0\x83\x90\x1d\x8a\r\xb2i\"-\xfb\\\xfe\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\u044e\xb9\xe1\u0485\u06be\x93\xe5\u053a\xe7k\xee\xfeC\xb5!\xe8\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\u0453\xe5\x83\xd6\a\x05c\xe7\xb8b\xb9aJG\u9509\xf3\xe5\x8965f3\xeb\xd8\xea\x00\x00\u07d4\u0457\x8f.4@\u007f\xab\x1d\xc2\x18=\x95\xcf\xdab`\xb3Y\x82\x89*\xb7\xb2`\xff?\xd0\x00\x00\u07d4\u045c\xaf9\xbb7\u007f\xdf,\xf1\x9b\xd4\xfbRY\x1c&1\xa6<\x8965\u026d\xc5\u07a0\x00\x00\u0794\u0463\x96\xdc\u06b2\xc7IA0\xb3\xfd0x 4\r\xfd\x8c\x1f\x88\xf9\"P\xe2\xdf\xd0\x00\x00\xe0\x94\u0467\x1b-\bX\xe82p\b]\x95\xa3\xb1T\x96P\x03^#\x8a\x03'\xbb\t\xd0j\xa8P\x00\x00\u07d4\u046c\xb5\xad\xc1\x189s%\x8dk\x85$\xff\xa2\x8f\xfe\xb2=\xe3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u0473\u007f\x03\xcb\x10t$\xe9\xc4\xddW\\\xcdOL\xeeW\xe6\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\u0475\xa4T\xac4\x05\xbbAy \x8cl\x84\xde\x00k\u02db\xe9\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xc4YT\xa6+\x91\x1a\xd7\x01\xff.\x90\x13\x1e\x8c\xeb\x89\xc9\\\x89K\x91\xa2\xdeE~\x88\x00\x00\u07d4\xd1\xc9np\xf0Z\xe0\xe6\xcd`!\xb2\b7P\xa7q|\xdeV\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\u0571\u007f\xfe-{\xbby\xcc}y0\xbc\xb2\xe5\x18\xfb\x1b\xbf\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd1\xda\f\x8f\xb7\xc2\x10\xe0\xf2\xeca\x8f\x85\xbd\xae}>sK\x1c\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd1\xddy\xfb\x15\x81`\xe5\xb4\xe8\xe2?1.j\x90\u007f\xbcMN\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd1\xdeZ\xad:_\xd8\x03\U00071bb6\x10<\xb8\xe1O\xe7#\xb7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd1\xe1\xf2\xb9\xc1l0\x98t\xde\xe7\xfa\xc3&u\xaf\xf1)\u00d8\x89\x03\xf2M\x8eJ\x00p\x00\x00\xe0\x94\xd1\xe5\xe24\xa9\xf4Bf\xa4\xa6$\x1a\x84\u05e1\xa5Z\u0567\xfe\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd1\xeaMr\xa6{[>\x0f1UY\xf5+\xd0aMq0i\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd1\xee\x90YW\xfe|\xc7\x0e\xc8\xf2\x86\x8bC\xfeG\xb1?\xeb\xff\x89\x02b\x9ff\xe0\xc50\x00\x00\u07d4\xd1\xf1iM\"g\x1bZ\xadj\x94\x99\\6\x9f\xbea3go\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd1\xf4\xdc\x1d\u06ca\xbb\x88H\xa8\xb1N%\xf3\xb5Z\x85\x91\xc2f\x89\r\x8drkqw\xa8\x00\x00\u07d4\xd1\xfe\u042e\xe6\xf5\xdf\xd7\xe2Wi%L<\xfa\xd1Z\xde\u032a\x89'\x92\xc8\xfcKS(\x00\x00\u07d4\xd2\x05\x1c\xb3\xcbg\x04\xf0T\x8c\u0210\xab\n\x19\xdb4\x15\xb4*\x89\x12\x1b.^ddx\x00\x00\u07d4\xd2\x06\xaa\u07736\xd4^yr\xe9<\xb0uG\x1d\x15\x89{]\x89 \x86\xac5\x10R`\x00\x00\u07d4\xd2\tH+\xb5I\xab\xc4w{\xeam\u007fe\x00b\xc9\xc5z\x1c\x89\x11e\x1a\xc3\xe7\xa7X\x00\x00\u07d4\xd2\r\xcb\vxh+\x94\xbc0\x00(\x14H\xd5W\xa2\v\xfc\x83\x890\x84\x9e\xbe\x166\x9c\x00\x00\u07d4\xd2\x10{57&\u00e2\xb4ef\xea\xa7\xd9\xf8\v]!\xdb\xe3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\x11\xb2\x1f\x1b\x12\xb5\ta\x81Y\r\xe0~\xf8\x1a\x89S~\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd2\x18\xef\xb4\u06d8\x1c\xddjy\u007fK\u050c|&)<\xeb@\x89\xa1Fk1\xc6C\x1c\x00\x00\xe0\x94\xd2\x1asA\xeb\x84\xfd\x15\x10T\xe5\u31fb%\xd3nI\x9c\t\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2$\xf8\x80\xf9G\x9a\x89\xd3/\t\xe5+\u9432\x88\x13\\\xef\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xd2/\f\xa4\xcdG\x9ef\x17u\x05;\xccI\xe3\x90\xf6p\u074a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd21\x92\x975\x13!\x02G\x1b\xa5\x90\a\xb6dL\xc0\xc1\xde>\x8967\tlK\xcci\x00\x00\u07d4\xd25\xd1\\\xb5\xec\xee\xbba)\x9e\x0e\x82\u007f\xa8'H\x91\x1d\x89\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2:$\xd7\xf9F\x83C\xc1C\xa4\x1ds\xb8\x8f|\xbec\xbe^\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2=z\xff\xac\xdc>\x9f=\xaez\xfc\xb4\x00oX\xf8\xa4F\x00\x89\xc3(\t>a\xee@\x00\x00\u07d4\xd2C\x18L\x80\x1e]y\xd2\x06?5x\u06ee\x81\u7ce9\u02c9k\u0722h\x1e\x1a\xba\x00\x00\u07d4\xd2KfD\xf49\xc8\x05\x1d\xfcd\u04c1\xb8\xc8lu\xc1u8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2K\xf1--\xdfE}\xec\xb1xt\xef\xde R\xb6\\\xbbI\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\xe0\x94\xd2Q\xf9\x03\xae\x18rrY\xee\xe8A\xa1\x89\xa1\xf5i\xa5\xfdv\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd2R\x96\v\v\xf6\xb2\x84\x8f\u07ad\x80\x13m\xb5\xf5\a\xf8\xbe\x02\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd2X\x1aU\xce#\xab\x10\u062d\x8cD7\x8fY\a\x9b\xd6\xf6X\x8a\x01\xdd\f\x88_\x9a\r\x80\x00\x00\u07d4\xd2Z\xec\xd7\xeb\x8b\xd64[\x06;]\xbd'\x1cw\xd3QD\x94\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xd2|#O\xf7\xac\xca\xce=\x99g\b\xf8\xf9\xb0Ip\xf9}6\x89Hz\x9a0E9D\x00\x00\u07d4\u0482\x98RM\xf5\xecK$\xb0\xff\xb9\u07c5\x17\n\x14Z\x9e\xb5\x89\x0f\x98\xa3\xb9\xb37\xe2\x00\x00\xe0\x94\u0483\xb8\xed\xb1\n%R\x8aD\x04\xde\x1ce\xe7A\r\xbc\xaag\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u0484\xa5\x03\x82\xf8:am9\xb8\xa9\xc0\xf3\x96\xe0\ubfe9]\x8966\xc2^f\xec\xe7\x00\x00\u07d4\u0488\xe7\xcb{\xa9\xf6 \xab\x0ftR\xe5\bc=\x1cZ\xa2v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u049d\xc0\x8e\xfb\xb3\xd7.&?x\xabv\x10\xd0\"m\xe7k\x00\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\u04a00\xac\x89R2_\x9e\x1d\xb3x\xa7\x14\x85\xa2N\x1b\a\xb2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u04a4y@CG\xc5T:\xab)*\xe1\xbbJo\x15\x83W\xfa\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u04a5\xa0$#\nW\xcc\xc6fv\v\x89\xb0\xe2l\xaf\u0449\u01ca\n\x96YZ\\n\x8a?\x80\x00\u07d4\u04a8\x03'\xcb\xe5\\L{\xd5\x1f\xf9\xdd\xe4\xcad\x8f\x9e\xb3\xf8\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\u04a8Oug\\b\xd8\f\x88ulB\x8e\xee+\xcb\x18T!\x89A\rXj \xa4\xc0\x00\x00\u07d4\u04ab\xd8J\x18\x10\x93\xe5\xe2)\x13oB\xd85\xe8#]\xe1\t\x89\x05k\xe0<\xa3\xe4}\x80\x00\u07d4\u04ac\r:X`^\x1d\x0f\x0e\xb3\xde%\xb2\xca\xd1)\xed`X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u04bfg\xa7\xf3\xc6\xceV\xb7\xbeAg]\xbb\xad\xfe~\xa9:3\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd2\xdb\xeb\xe8\x9b\x03W\xae\xa9\x8b\xbe\x8eIc8\u07bb(\xe8\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd2\xe2\x1e\xd5hh\xfa\xb2\x8e\tG\x92z\xda\xf2\x9f#\xeb\xadl\x89l\x18O\x13U\xd0\xe8\x00\x00\u07d4\xd2\xe8\x17s\x8a\xbf\x1f\xb4\x86X?\x80\xc3P1\x8b\xed\x86\f\x80\x89\r\x02\xce\xcf_]\x81\x00\x00\u07d4\xd2\xed\xd1\xdd\xd6\xd8m\xc0\x05\xba\xebT\x1d\"\xb6@\xd5\xc7\xca\xe5\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd2\xf1\x99\x8e\x1c\xb1X\f\xecOl\x04}\xcd=\xce\xc5L\xf7<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd2\xf2A%]\xd7\xc3\xf7<\a\x040q\xec\b\xdd\xd9\xc5\xcd\xe5\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd2\xffg \x16\xf6;/\x859\x8fJo\xed\xbb`\xa5\r<\u0389\x12\x91$o[sJ\x00\x00\u07d4\xd3\rLC\xad\xcfU\xb2\xcbS\u0583#&A4I\x8d\x89\u038965\u026d\xc5\u07a0\x00\x00\u07d4\xd3\x0e\xe9\xa1+Mh\xab\xac\xe6\xba\u029a\u05ff\\\xd1\xfa\xf9\x1c\x89QO\xcb$\xff\x9cP\x00\x00\u07d4\xd3\x11\x8e\xa3\xc85\x05\xa9\u0613\xbbg\xe2\xde\x14-Sz>\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd3\x11\xbc\u05eaN\x9bO8?\xf3\xd0\u05b6\xe0~!\xe3p]\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd3\x15\xde\xea\x1d\x8c\x12q\xf9\xd11\x12c\xabG\xc0\a\xaf\xb6\xf5\x89\x03\xc8\x1dNeK@\x00\x00\u07d4\xd3+,y\xc3dx\xc5C\x19\x01\xf6\xd7\x00\xb0M\xbe\x9b\x88\x10\x89\x15w\x9a\x9d\xe6\xee\xb0\x00\x00\u07d4\xd3+EVF\x14Ql\x91\xb0\u007f\xa9\xf7-\xcfx|\xceN\x1c\x89\x0f\xc6o\xae7F\xac\x00\x00\u07d4\xd30r\x811\xfe\x8e:\x15Hz4W<\x93E~*\xfe\x95\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd31\xc8#\x82Z\x9eRc\xd0R\u0611]M\xcd\xe0z\\7\x89\x1e\x93\x12\x83\xcc\xc8P\x00\x00\u07d4\xd33btE\xf2\u05c7\x90\x1e\xf3;\xb2\xa8\xa3g^'\xff\xec\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3<\xf8+\xf1LY&@\xa0\x86\b\x91L#py\u057e4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3Mp\x8ds\x98\x02E3\xa5\xa2\xb20\x9b\x19\xd3\xc5Qq\xbb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd3N\x03\xd3j+\xd4\u045a_\xa1b\x18\xd1\xd6\x1e?\xfa\v\x15\x89\x11X\xe4`\x91=\x00\x00\x00\u07d4\xd3Pu\xcaa\xfeY\xd1#\x96\x9c6\xa8-\x1a\xb2\xd9\x18\xaa8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xd3g\x00\x9a\xb6X&;b\xc23:\x1c\x9eA@I\x8e\x13\x89\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd3g\x9aG\xdf-\x99\xa4\x9b\x01\u024d\x1c>\f\x98|\xe1\xe1X\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\xe0\x94\u04cf\xa2\xc4\xcc\x14z\xd0j\u0562\xf7Uy(\x1f\"\xa7\xcc\x1f\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\u04da]\xa4`9+\x94\v<i\xbc\x03u{\xf3\xf2\xe8$\x89\x8a\x01|\x83\xa9}kl\xa5\x00\x00\u07d4\u04db|\xbc\x94\x00?\xc9H\xf0\xcd\xe2{\x10\r\xb8\xcc\xd6\xe0c\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u04e1\x0e\u01e5\xc92I\x99\u075e\x9bk\xde|\x91\x1eXK\u0689 \x86\xac5\x10R`\x00\x00\u07d4\u04e9A\xc9a\xe8\u028b\x10p\xf2<mm\r*u\x8aDD\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u04fbY\xfa1%\x8b\xe6/\x8e\xd22\xf1\xa7\xd4{J\vA\xee\x89\x05k\xc7^-c\x10\x00\x00\u07d4\u04fcs\t7\xfau\xd8E&\x16\xad\x1e\xf1\xfe\u007f\xff\xe0\xd0\xe7\x89\x04\x84\xe4\xde\xd2\xea\xe3\x80\x00\u07d4\xd3\xc2MK:^\x0f\xf8\xa4b-Q\x8e\xdds\xf1j\xb2\x86\x10\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd3\xc6\xf1\xe0\xf5\x0e\xc3\u04a6~k\xcd\x19>\u01ee8\xf1e\u007f\x8a\x01f\xc5H\b\x89\xdbw\x00\x00\xe0\x94\xd3\xd6\xe9\xfb\x82T/\u049e\xd9\xea6\t\x89\x1e\x15\x13\x96\xb6\xf7\x8a\voX\x8a\xa7\xbc\xf5\xc0\x00\x00\xe0\x94\xd3\xda\u0476\u040dE\x81\u032ee\xa8s-\xb6\xaci\xf0\u019e\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xd3\xdf;S\xcb;GU\xdeT\xe1\x80E\x1c\xc4L\x9e\x8a\u0a89#\u0114\t\xb9w\x82\x80\x00\u07d4\xd3\xf8s\xbd\x99V\x13W\x89\xab\x00\xeb\xc1\x95\xb9\"\xe9K%\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\x02\xb4\xf6\xa0\x99\xeb\xe7\x16\xcb\x14\xdfOy\xc0\xcd\x01\xc6\a\x1b\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd4\r\x00U\xfd\x9a8H\x8a\xff\x92?\xd0=5\xecF\xd7\x11\xb3\x8a\x01\x0f\b\xed\xa8\xe5U\t\x80\x00\u07d4\xd4\x0e\xd6j\xb3\xce\xff$\xca\x05\xec\xd4q\ufd12\xc1__\xfa\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4\x18\x87\v\xc2\xe4\xfa{\x8aa!\xae\br\xd5RG\xb6%\x01\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xd4\x1d\u007f\xb4\x9f\xe7\x01\xba\xac%qpBl\u0273\x8c\xa3\xa9\xb2\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xd4 U\x92\x84@U\xb3\u01e1\xf8\f\xef\xe3\xb8\xebP\x9b\xcd\xe7\x89\t\xb3\xbf\xd3B\xa9\xfc\x80\x00\u07d4\xd4+ \xbd\x03\x11`\x8bf\xf8\xa6\xd1[*\x95\xe6\xde'\u017f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd44O}\\\xade\xd1~\\-\x0es#\x94=ob\xfe\x92\x89\x0e~\xeb\xa3A\vt\x00\x00\u07d4\xd4>\xe48\xd8=\xe9\xa3ub\xbbN(l\xb1\xbd\x19\xf4\x96M\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd4C4\xb4\xe2:\x16\x9a\f\x16\xbd!\xe8f\xbb\xa5-\x97\x05\x87\x89\x8c\xf2?\x90\x9c\x0f\xa0\x00\x00\xe0\x94\xd4M\x81\xe1\x8fF\xe2\u03f5\xc1\xfc\xf5\x04\x1b\xc8V\x97g\xd1\x00\x8a\a\xb4B\xe6\x84\xf6Z\xa4\x00\x00\u07d4\xd4OJ\xc5\xfa\xd7k\xdc\x157\xa3\xb3\xafdr1\x9bA\r\x9d\x89V\xbcu\xe2\xd61\x00\x00\x00\u07d4\xd4O^\xdf+\xcf$3\xf2\x11\xda\xdd\f\xc4P\xdb\x1b\x00\x8e\x14\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xd4Oj\u00d2;_\xd71\xa4\xc4YD\xecO~\xc5*j\xe4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd4[3A\xe8\xf1\\\x802\x93 \u00d7~;\x90\xe7\x82j~\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xd4]]\xaa\x13\x8d\xd1\xd3t\xc7\x1b\x90\x19\x91h\x11\xf4\xb2\nN\x89\x1f9\x9b\x148\xa1\x00\x00\x00\u07d4\xd4`\xa4\xb9\b\xdd+\x05gY\xb4\x88\x85\vf\xa88\xfcw\xa8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd4g\xcf\x06L\bq\x98\x9b\x90\u0632\xeb\x14\xcc\xc6;6\b#\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd4k\xaea\xb0'\xe5\xbbB.\x83\xa3\xf9\xc9?<\x8f\xc7}'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4o\x82#E)\x82\xa1\xee\xa0\x19\xa8\x81n\xfc-o\xc0\ah\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xd4uG\u007f\xa5c\x90\xd30\x17Q\x8dg\x11\x02\u007f\x05\U0008dfc9k\x11\x133\xd4\xfdL\x00\x00\u07d4\xd4|$.\xdf\xfe\xa0\x91\xbcT\xd5}\xf5\xd1\xfd\xb91\x01Gl\x89\x9d\xf7\u07e8\xf7`H\x00\x00\u07d4\xd4}\x86\x85\xfa\xee\x14|R\x0f\u0646p\x91u\xbf/\x88k\xef\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\u007fP\u07c9\xa1\xcf\xf9e\x13\xbe\xf1\xb2\xae:)q\xac\xcf,\x89-\x89W}}@ \x00\x00\u07d4\u0502\xe7\xf6\x8eA\xf28\xfeQx)\xde\x15G\u007f\xe0\xf6\xdd\x1d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u0507\x9f\xd1+\x1f:'\xf7\xe1\tv\x1b#\xca4<H\xe3\u0609$\x1a\x9bOaz(\x00\x00\u07d4\u050e?\x93W\xe3\x03Q8A\xb3\xf8K\u0683\xfc\x89ru\x87\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\u051au\xbb\x93?\xca\x1f\u029a\xa10:d\xb6\xcbD\xea0\xe1\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u0530\x85\xfb\bo=\rh\xbf\x12\x92k\x1c\xc3\x14,\xae\x87p\x89\u0213\u041c\x8fQP\x00\x00\u07d4\u0532\xff;\xae\x19\x93\xff\xeaM;\x18\x021\xdaC\x9fu\x02\xa2\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u0533\x8a_\xdbc\xe0\x17\x14\xe9\x80\x1d\xb4{\u0250\xbdP\x91\x83\x8a\x01E4\xd9[\xef\x90\\\x00\x00\u07d4\u0538\xbd\xf3\u07daQ\xb0\xb9\x1d\x16\xab\xbe\xa0[\xb4x<\x86a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd4\xc4\u0467\xc3\xc7I\x84\xf6\x85{/_\a\xe8\xfa\xceh\x05m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd4\u01act.|\x85}J\x05\xa0L3\xd4\xd0\\\x14gW\x1d\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd4\xcb!\xe5\x90\u0160\xe0h\x016j\xff4,}}\xb1d$\x89\x1a\u01e0\x8e\xad\x02\xf8\x00\x00\u07d4\xd4\xd9,b\xb2\x80\xe0\x0fbm\x86W\xf1\xb8af\xcb\x1ft\x0f\x89\n\xd7\xf264\xcb\xd6\x00\x00\u07d4\xd4\ubc52\x9a#\x87\x1c\xf7\u007f\xe0I\xab\x96\x02\xbe\b\xbe\ns\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xd4\xeeI\x19\xfb7\xf2\xbb\x97\f?\xffT\xaa\xf1\xf3\u0766\xc0?\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xd4\xfe\xed\x99\xe8\x91|\\TXc_6\x03\xec\xb7\xe8\x17\xa7\u0409\x10C\xc4<\xde\x1d9\x80\x00\u07d4\xd4\xffF >\xfa#\x06K\x1c\xaf\x00Qn(pJ\x82\xa4\xf8\x89Hz\x9a0E9D\x00\x00\u07d4\xd5\x00\xe4\xd1\u0242K\xa9\xf5\xb65\u03e3\xa8\xc2\u00cb\xbdL\xed\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5\b\u04dcp\x91oj\xbcL\xc7\xf9\x99\xf0\x11\xf0w\x10X\x02\x89\x05rM$\xaf\xe7\u007f\x00\x00\u07d4\xd5\x0f\u007f\xa0>8\x98v\u04d0\x8b`\xa57\xa6pc\x04\xfbV\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd5\x13\xa4P\x80\xff/\xeb\xe6,\u0545J\xbe)\xeeDg\xf9\x96\x89\bN\x13\xbcO\xc5\xd8\x00\x00\u07d4\xd5'o\f\xd5\xff\xd5\xff\xb6?\x98\xb5p=U\x94\xed\xe0\x83\x8b\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xd5)KfbB0;m\xf0\xb1\u020d7B\x9b\xc8\xc9e\xaa\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xd5*\xec\xc6I98\xa2\x8c\xa1\xc3g\xb7\x01\xc2\x15\x98\xb6\xa0.\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xd5<V\u007f\f?\xf2\xe0\x8b}Y\xe2\xb5\xc74\x85C\u007f\u014d\x89 \x86\xac5\x10R`\x00\x00\u07d4\xd5A\xac\x18z\xd7\xe0\x90R-\xe6\xda2\x13\xe9\xa7\xf4C\x96s\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd5K\xa2\xd8V\x81\xdc\x13\x0e[\x9b\x02\xc4\xe8\xc8Q9\x1f\u0679\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xd5U\b\xad\xbb\xbe\x9b\xe8\x1b\x80\xf9zn\xa8\x9a\xddh\xdagO\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd5U\f\xaa\xf7C\xb07\xc5o\xd2U\x8a\x1c\x8e\xd25\x13\aP\x8a\x01!\xe4\u05106%[\x00\x00\u07d4\xd5Xm\xa4\u5543\xc8\xd8l\xcc\xf7\x1a\x86\x19\u007f\x17\x99gI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd5\\\x1c\x8d\xfb\xe1\xe0,\xac\xbc\xa6\x0f\xdb\xdd@[\t\xf0\xb7_\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd5a\u02fc\x05Q]\xe7:\xb8\u03de\xae\x13W4\x1e}\xfd\xf4\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xd5j\x14Mz\xf0\xae\x8d\xf6I\xab\xaeSZ\x15\x98:\xa0M\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xd5r0\x91i\xb1@.\xc8\x13\x1a\x17\xa6\xaa\xc3\"/\x89\xe6\xeb\x8a\x02\xec\x19x\xc4wf\xa0\x00\x00\u07d4\xd5xvh\xc2\xc5\x17[\x01\xa8\xee\x1a\xc3\xec\xc9\u0232\xab\xa9Z\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\u0548\u00e5\xdf\"\x81\x85\u064e\xe7\xe6\aH%\\\u07a6\x8b\x01\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u054aR\xe0x\xa8\x05Yk\rV\xeaJ\xe13Z\xf0\x1cf\xeb\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\u0550>\x99x\xee \xa3\x8c?I\x8dc\xd5\u007f1\xa3\x9fj\x06\x8a\x022\xb3o\xfcg*\xb0\x00\x00\u07d4\u05568\xd3\xc5\xfa\xa7q\x1b\xf0\x85t_\x9d[\xdc#\u0518\u0609lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u055d\x92\xd2\xc8p\x19\x80\xcc\a<7]r\n\xf0dt<\f\x8a\x04\x05\xfd\xf7\u5bc5\xe0\x00\x00\u07d4\u0567\xbe\xc32\xad\xde\x18\xb3\x10KW\x92Tj\xa5\x9b\x87\x9bR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0571\x17\xec\x11n\xb8FA\x89a\xeb~\xdbb\x9c\xd0\xddi\u007f\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u0572\x84\x04\x010\xab\xf7\xc1\xd1cq#q\xcc~(\xadf\u0689j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u0579\xd2w\u062a\xd2\x06\x97\xa5\x1fv\xe2\tx\x99k\xff\xe0U\x89\a\xc3\xfe<\aj\xb5\x00\x00\u07d4\u057d^\x84U\xc10\x16\x93W\xc4q\xe3\u6077\x99jrv\x89-\x9e(\x8f\x8a\xbb6\x00\x00\u07d4\xd5\u02e5\xb2k\xea]s\xfa\xbb\x1a\xba\xfa\xcd\xef\x85\xde\xf3h\u0309\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd5\xceU\u0476/YC<!&\xbc\xec\t\xba\xfc\x9d\xfa\xa5\x14\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xd5\xe5Q\x00\xfb\u0455k\xbe\xd2\xcaQ\x8dK\x1f\xa3v\x03+\v\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd5\xe5\xc15\xd0\xc4\xc3094q\x19\x93\xd0\xd1o\xf9\u7ea0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd5\xe6V\xa1\xb9\x16\xf9\xbfE\xaf\xb0}\u062f\xafs\xb4\xc5oA\x89\x05B%:\x12l\xe4\x00\x00\xe0\x94\xd5\xeaG,\xb9F`\x18\x11\n\xf0\f7I[\\,q1\x12\x8a\x01\x0e\xeehl\x85OD\x00\x00\u07d4\xd5\xf0uR\xb5\u0193\xc2\x00g\xb3x\xb8\t\xce\xe8S\xb8\xf16\x89\x1bg\xc6\u07c8\xc6\xfa\x00\x00\u07d4\xd5\xf7\xc4\x1e\ar\x9d\xfam\xfcd\xc4B1`\xa2,`\x9f\u04c9a\t=|,m8\x00\x00\u07d4\xd6\x04\xab\xceC0\x84.=9l\xa7=\xdbU\x19\xed>\xc0?\x89\b\xe3\x1f\xe1h\x9d\x8a\x00\x00\u07d4\xd6\x06Q\xe3\x93x4#\xe5\xcc\x1b\xc5\xf8\x89\xe4N\xf7\xea$>\x89\x15\x9ev7\x11)\xc8\x00\x00\u07d4\xd6\t\xbfO\x14n\xeak\r\xc8\xe0m\xdc\xf4D\x8a\x1f\xcc\xc9\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\t\xec\v\xe7\r\n\xd2ong\xc9\xd4v+R\xeeQ\x12,\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6\nRX\a(R\r\xf7Tk\xc1\xe2\x83)\x17\x88\u06ee\f\x8964\x89\xef?\xf0\xd7\x00\x00\u07d4\xd6\v$s!\xa3*Z\xff\xb9k\x1e'\x99'\xccXM\xe9C\x89z\xd0 \xd6\xdd\xd7v\x00\x00\u07d4\xd6\x11\x02v\xcf\xe3\x1eB\x82ZW\u007fkC]\xbc\xc1\f\xf7d\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd6\x12Y{\xc3\x17C\u01c63\xf63\xf29\xb1\xe9Bk\xd9%\x8a\x10\x17\xf7\u07d6\xbe\x17\x80\x00\x00\u07d4\xd6#J\xafE\xc6\xf2.f\xa2%\xff\xb9:\xddb\x9bN\xf8\x0f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd6.\u06d6\xfc\u259a\xaflT^\x96|\xf1\xc0\xbc\x80R\x05\x89\x04\xa5eSjZ\u0680\x00\u07d4\xd60\v2\x15\xb1\x1d\xe7b\xec\xdeKp\xb7\x92}\x01)\x15\x82\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd69]\xb5\xa4\xbbf\xe6\x0fL\xfb\xcd\xf0\x05{\xb4\xd9xb\xe2\x891T\xc9r\x9d\x05x\x00\x00\xe0\x94\xd6J-P\xf8\x85\x857\x18\x8a$\xe0\xf5\r\xf1h\x1a\xb0~\u05ca\b7Z*\xbc\xca$@\x00\x00\u07d4\xd6X\n\xb5\xedL}\xfaPo\xa6\xfed\xad\\\xe1)pw2\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6Y\x8b\x13\x86\xe9<\\\u02d6\x02\xffK\xbb\xec\xdb\xd3p\x1d\u0109\f%\xf4\xec\xb0A\xf0\x00\x00\u07d4\xd6dM@\xe9\v\xc9\u007f\xe7\xdf\xe7\u02bd2i\xfdW\x9b\xa4\xb3\x89\b\x9e\x91y\x94\xf7\x1c\x00\x00\xe0\x94\xd6g\f\x03m\xf7T\xbeC\xda\u074fP\xfe\xea(\x9d\x06\x1f\u058a\x01D\xa2\x904H\xce\xf7\x80\x00\u07d4\xd6hR:\x90\xf0)=e\xc58\xd2\xddlWg7\x10\x19n\x89\x02$,0\xb8S\xee\x00\x00\u07d4\xd6j\xb7\x92\x94\aL\x8bb}\x84-\xabA\xe1}\xd7\f]\xe5\x8965\u026d\xc5\u07a0\x00\x00\u0794\xd6j\xcc\r\x11\xb6\x89\u03a6\xd9\xea_\xf4\x01L\"J]\xc7\u0108\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xd6m\xdf\x11Y\xcf\"\xfd\x8czK\xc8\u0540wV\xd43\xc4>\x89wC\"\x17\xe6\x83`\x00\x00\u07d4\u0587\xce\xc0\x05\x90\x87\xfd\xc7\x13\xd4\xd2\xd6^w\xda\xef\xed\xc1_\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\u0588\xe7\x85\u024f\x00\xf8K:\xa1S3U\u01e2X\xe8yH\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u05a2.Y\x8d\xab\u04ce\xa6\xe9X\xbdy\u050d\u0756\x04\xf4\u07c965\u026d\xc5\u07a0\x00\x00\u07d4\u05a7\xacM\xe7\xb5\x10\xf0\xe8\xdeQ\x9d\x97?\xa4\xc0\x1b\xa84\x00\x89e\xea=\xb7UF`\x00\x00\u07d4\u05ac\xc2 \xba.Q\xdf\xcf!\xd4C6\x1e\xeav\\\xbd5\u0609\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05ac\xff\u043f\u065c8.{\xd5o\xf0\xe6\x14J\x9eR\xb0\x8e\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xd6\xc0\u043c\x93\xa6.%qtp\x0e\x10\xf0$\u0232?\x1f\x87\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd6\xcf\\\x1b\u03dd\xa6b\xbc\xea\"U\x90P\x99\xf9\xd6\xe8M\u030a\x01\u011eB\x01W\xd9\xc2\x00\x00\u07d4\xd6\xd05r\xa4RE\xdb\xd46\x8cO\x82\xc9W\x14\xbd!g\xe2\x89?\x00\xc3\xd6f\x86\xfc\x00\x00\u07d4\xd6\xd6wiX\xee#\x14:\x81\xad\xad\xeb\b8 \t\xe9\x96\u0089\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd6\xd9\xe3\x0f\bB\x01*qv\xa9\x17\xd9\xd2\x04\x8c\xa0s\x87Y\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd6\xe0\x9e\x98\xfe\x13\x003!\x04\xc1\xca4\xfb\xfa\xc5T6N\u0649lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\xe8\xe9z\u90db\x9e\xe5\a\xee\xdb(\xed\xfbtw\x03\x149\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd6\uea18\u052e+q\x80'\xa1\x9c\xe9\xa5\xebs\x00\xab\xe3\u0289\x01}J\xce\xeec\u06c0\x00\xe0\x94\xd6\xf1\xe5[\x16\x94\b\x9e\xbc\xb4\xfe}x\x82\xaaf\u0217av\x8a\x04<#\xbd\xbe\x92\x9d\xb3\x00\x00\u07d4\xd6\xf4\xa7\xd0N\x8f\xaf \xe8\xc6\ub15c\xf7\xf7\x8d\xd2=z\x15\x89\a$\xde\xd1\xc7H\x14\x00\x00\u07d4\xd6\xfc\x04F\u01a8\xd4\n\xe3U\x1d\xb7\xe7\x01\xd1\xfa\x87nJI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x03\u01a4\xf1\x1d`\x19Ey\u054c'f\xa7\xef\x16\xc3\n)\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\x05%\x19uj\xf4%\x90\xf1S\x91\xb7#\xa0?\xa5d\xa9Q\x89\xfa61H\r\x01\xfd\x80\x00\u07d4\xd7\na+\xd6\u0769\xea\xb0\xdd\xdc\xffJ\xafA\"\u04cf\xea\xe4\x89\x1dF\x01b\xf5\x16\xf0\x00\x00\u07d4\xd7\n\xd2\xc4\xe9\uefe67\xefV\xbdHj\u04a1\xe5\xbc\xe0\x93\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x14\f\x8eZC\a\xfa\xb0\xcc'\xba\u0752\x95\x01\x8b\xf8yp\x89\x05\xf1\x01kPv\xd0\x00\x00\u07d4\xd7\x16J\xa2a\xc0\x9a\u0672\xb5\x06\x8dE>\xd8\xebj\xa10\x83\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\x1eC\xa4Qw\xadQ\xcb\xe0\xf7!\x84\xa5\xcbP9\x17(Z\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\x1f\xb10\xf0\x15\fVRi\xe0\x0e\xfbC\x90+R\xa4U\xa6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\"W8\xdc\xf3W\x848\xf8\xe7\u0233\x83~B\xe0J&/\x89\x18+\x8c\ubec3\xaa\x00\x00\u07d4\xd7'MP\x80M\x9cw\u0693\xfaH\x01V\xef\xe5{\xa5\x01\u0789i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xd71\xbbk_<79^\t\u03ac\xcd\x14\xa9\x18\xa6\x06\a\x89\x89\u0556{\xe4\xfc?\x10\x00\x00\xe0\x94\xd7>\xd2\u0645\xb5\xf2\x1bU\xb2td;\xc6\xda\x03\x1d\x8e\u074d\x8a\nm\xd9\f\xaeQ\x14H\x00\x00\u07d4\xd7D\xac~S\x10\xbeijc\xb0\x03\xc4\v\xd097\x05a\u0189Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\xd7Jn\x8dj\xab4\u0385\x97h\x14\xc12{\xd6\xea\a\x84\u048a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xd7ZP*[gr\x87G\x0fe\u016aQ\xb8|\x10\x15\x05r\x8910\xb4dc\x85t\x00\x00\u07d4\xd7m\xba\xeb\xc3\rN\xf6{\x03\xe6\xe6\xec\xc6\xd8N\x00MP-\x89mv\xb9\x18\x8e\x13\x85\x00\x00\u07d4\xd7q\xd9\xe0\u028a\b\xa1\x13wW1CN\xb3'\x05\x99\xc4\r\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7x\x8e\xf2\x86X\xaa\x06\xccS\xe1\xf3\xf0\xdeX\xe5\xc3q\xbex\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xd7x\x92\xe2';#]v\x89\xe40\xe7\xae\ud73c\xe8\xa1\xf3\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u05c1\xf7\xfc\t\x18F\x11V\x85p\xb4\x98n,r\x87+~\u0409\x01\x15\x95a\x06]]\x00\x00\u07d4\u05c5\xa8\xf1\x8c8\xb9\xbcO\xfb\x9b\x8f\xa8\xc7r{\xd6B\xee\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u05ce\xcd%\xad\xc8k\xc2\x05\x1d\x96\xf6Sd\x86kB\xa4&\xb7\x89\xd20X\xbf/&\x12\x00\x00\xe0\x94\u05cf\x84\xe3\x89D\xa0\xe0%_\xae\xceH\xbaIP\u053d9\u048a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\u05d4\x83\xf6\xa8DO%I\xd6\x11\xaf\xe0,C-\x15\xe1\x10Q\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u05d85\xe4\x04\xfb\x86\xbf\x84_\xba\t\rk\xa2^\f\x88f\xa6\x89\x82\x1a\xb0\xd4AI\x80\x00\x00\u07d4\u05da\xff\x13\xba-\xa7]F$\f\xac\n$g\xc6V\x94\x98#\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\u05dd\xb5\xabCb\x1az=\xa7\x95\xe5\x89)\xf3\xdd%\xafg\u0649lj\xccg\u05f1\xd4\x00\x00\u07d4\u05e1C\x1e\xe4S\xd1\xe4\x9a\x05P\xd1%hy\xb4\xf5\xd1\x02\x01\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\u05ed\t\xc6\xd3&WhSU\xb5\xc6\uc39fW\xb4\ube42\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u05f7@\xdf\xf8\xc4Wf\x8f\xdft\xf6\xa2f\xbf\xc1\u0737#\xf9\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xd7\u0080>\u05f0\xe0\x83sQA\x1a\x8ef7\xd1h\xbc[\x05\x8a\x06A\xda\xf5\xc9\x1b\xd95\x80\x00\u07d4\xd7\xc6&]\xea\x11\x87l\x90;q\x8eL\u062b$\xfe&[\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xca\u007f\xdc\xfe\xbeE\x88\xef\xf5B\x1d\x15\"\xb6\x13(\xdf{\xf3\x89\xd8\xe6\x00\x1el0+\x00\x00\u07d4\xd7\u037dA\xff\xf2\r\xf7'\xc7\vbU\xc1\xbav\x06\x05Th\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xd1W\xe4\xc0\xa9d7\xa6\u0485t\x1d\xd2>\xc46\x1f\xa3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xd2\xc6\xfc\xa8\xad\x1fu9R\x10\xb5}\xe5\xdf\xd6s\x939\t\x89\x12nr\xa6\x9aP\xd0\x00\x00\xe0\x94\xd7\xd3\xc7Y Y\x048\xb8,>\x95\x15\xbe.\xb6\xedz\x8b\x1a\x8a\f\xb4\x9bD\xba`-\x80\x00\x00\u07d4\xd7\xd7\xf2\u02a4b\xa4\x1b;0\xa3J\xeb;\xa6\x10\x10\xe2bo\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd7\xe7J\xfd\xba\xd5^\x96\u03bcZ7O,\x8bv\x86\x80\xf2\xb0\x89\x05]\xe6\xa7y\xbb\xac\x00\x00\xe0\x94\xd7\xeb\x901b'\x1c\x1a\xfa5\xfei\xe3s\"\u0224\u049b\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xd7\xeb\u0779\xf99\x87w\x9bh\x01U7T8\xdbe\xaf\xcbj\x89\x05t\x1a\xfe\xff\x94L\x00\x00\u07d4\xd7\xef4\x0ef\xb0\u05ef\xcc\xe2\n\x19\xcb{\xfc\x81\xda3\xd9N\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd7\xf3p\u053e\xd9\xd5|oI\u0259\xder\x9e\xe5i\xd3\xf4\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd7\xfa_\xfb`H\xf9o\xb1\xab\xa0\x9e\xf8{\x1c\x11\xddp\x05\xe4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\x06\x9f\x84\xb5!I?G\x15\x03\u007f2&\xb2_3\xb6\x05\x86\x89g\x8a\x93 b\xe4\x18\x00\x00\u0794\xd8\x15\xe1\xd9\xf4\xe2\xb5\xe5~4\x82k|\xfd\x88\x81\xb8Th\x90\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xd8\x1b\xd5K\xa2\xc4Jok\xeb\x15a\u058b\x80\xb5DNm\u0189?\x17\r~\xe4<C\x00\x00\xe0\x94\xd8\"QEm\xc18\x0f\x8fV\x92\xf9b\x82\x86@\xab\x9f*\x03\x8a\x01\b\x8bS\xb2\xc2\x02\xbe\x00\x00\u07d4\xd8,o\xed\xbd\xac\x98\xaf.\xed\x10\xb0\x0f2\xb0\x00V\xcaZm\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd8/\xd9\xfd\xf6\x99k\xed\xad(C\x15\x9c\x06\xf3~\t$3}\x89[\x8c\xce\xdcZ\xa7\xb0\x00\x00\u07d4\xd8:\xd2`\xe9\xa6\xf42\xfbn\xa2\x87C)\x9bJ\t\xade\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u0794\xd8C\xee\bc\u0393>\"\xf8\x9c\x80-1({\x96q\xe8\x1c\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xd8K\x92/xA\xfcWt\xf0\x0e\x14`J\xe0\xdfB\xc8U\x1e\x89\xd9o\u0390\u03eb\xcc\x00\x00\u07d4\xd8U\xb0<\xcb\x02\x9awG\xb1\xf0s\x03\xe0\xa6dy59\u0209lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8_\u07af*a\xf9]\xb9\x02\xf9\xb5\xa5<\x9b\x8f\x92f\u00ec\x89l\xf6Z~\x90G(\x00\x00\u07d4\xd8q^\xf9\x17o\x85\v.0\xeb\x8e8'\a\xf7w\xa6\xfb\xe9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd8t\xb9\u07eeEj\x92\x9b\xa3\xb1\xa2~W,\x9b,\xec\u07f3\x89\t79SM(h\x00\x00\u07d4\u0613\n9\xc7sW\xc3\n\u04e0`\xf0\v\x06\x04c1\xfdb\x89,s\xc97t,P\x00\x00\u07d4\u061b\xc2q\xb2{\xa3\xabib\xc9JU\x90\x06\xae8\xd5\xf5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0637}\xb9\xb8\x1b\xbe\x90B{b\xf7\x02\xb2\x01\xff\u009f\xf6\x18\x892m\x1eC\x96\xd4\\\x00\x00\u07d4\xd8\xcdd\xe0(N\xecS\xaaF9\xaf\xc4u\b\x10\xb9\u007f\xabV\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xd6C\x84$\x9bwg\x94\x06;V\x98x\xd5\xe3\xb50\xa4\xb2\x89\t\xa0C\u0432\xf9V\x80\x00\u07d4\xd8\xd6T \xc1\x8c#'\xccZ\xf9t%\xf8W\xe4\xa9\xfdQ\xb3\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xd8\xe5\xc9g^\xf4\xde\xed&k\x86\x95o\xc4Y\x0e\xa7\u0522}\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xd8\xe8GB\x92\xe7\xa0Q`L\xa1d\xc0pw\x83\xbb(\x85\xe8\x8a\x02\xd4\xca\x05\xe2\xb4<\xa8\x00\x00\u07d4\xd8\xebxP>\xc3\x1aT\xa9\x016x\x1a\xe1\t\x00Lt2W\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd8\xee\xf4\xcfK\xeb\x01\xee \xd1\x11t\x8ba\xcbM?d\x1a\x01\x89\x94\x89#z\u06daP\x00\x00\u07d4\xd8\xf4\xba\xe6\xf8M\x91\rm}Z\xc9\x14\xb1\xe6\x83r\xf9A5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xd8\xf6 6\xf0;v5\xb8X\xf1\x10?\x8a\x1d\x90\x19\xa8\x92\xb6\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xd8\xf6e\xfd\x8c\xd5\u00bc\xc6\xdd\xc0\xa8\xaeR\x1eM\u01aa``\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xd8\xf9$\fU\xcf\xf05R<m[\xd3\x00\xd3p\u070f\f\x95\x89\x0fs+f\x01ZT\x00\x00\xe0\x94\xd8\xf9EyIg%\xb5\xcbS\u05d8\\\x98\x97I\xaf\xf8I\xc0\x8a\x03\x99\x92d\x8a#\u0220\x00\x00\u07d4\xd8\xfd\xf5FgG8\u0244\xd8\xfa\xb8W\x88\v>B\x80\xc0\x9e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xd8\xfe\b\x8f\xff\u0394\x8fQ7\xee#\xb0\x1d\x95\x9e\x84\xacB#\x89\f[T\xa9O\xc0\x17\x00\x00\u07d4\xd9\x0f0\t\xdbC~N\x11\u01c0\xbe\u0209os\x8de\xef\r\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\x10;\xb6\xb6zU\xa7\xfe\xce-\x1a\xf6-E|!x\x94m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\x13\xf0w\x19Iu<G&\xac\xaa+\xd3a\x9c\\ \xffw\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xd9\x1d\x88\x91dG\x9c\xe46\xec\xe5\x17c\xe2,\xda\x19\xb2-k\x89\xb6m\x88\x12h\x00\x88\x00\x00\u07d4\xd9)\xc6]i\u057b\xae\xa5\x97bf.\xf4\x18\xbc!\xad\x92J\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd90\xb2zx\x87d\x85\xd0\xf4\x8bp\xddS6T\x96y\u028f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xd91\xac&h\xbaj\x84H\x1a\xb19sZ\xec\x14\xb7\xbf\xba\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd98=Km\x17\xb3\xf9\xcdBn\x10\xfb\x94@\x15\xc0\xd4K\xfb\x89+^:\xf1k\x18\x80\x00\x00\xe0\x94\xd9B\xdeG\x84\xf7\xa4\x87\x16\xc0\xfdK\x9dT\xa6\xe5L_/>\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xd9D\u0226\x9f\xf2\xca\x12Ii\f\x12)\xc7\x19/6%\x10b\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xd9JW\x88*Rs\x9b\xbe*\x06G\xc8\f$\xf5\x8a+O\x1c\x89H\xb5N*\xdb\xe1+\x00\x00\xe0\x94\xd9SB\x95<\x8a!\xe8\xb65\xee\xfa\u01c1\x9b\xea0\xf1pG\x8a\x13\xf0l\u007f\xfe\xf0]@\x00\x00\u07d4\xd9\\\x90\xff\xbeT\x84\x86G\x80\xb8gIJ\x83\u0212V\xd6\xe4\x89X\xe7\x92n\xe8X\xa0\x00\x00\u07d4\xd9g\x11T\x0e.\x99\x83C\xd4\xf5\x90\xb6\xfc\x8f\xac;\xb8\xb3\x1d\x89_Z@h\xb7\x1c\xb0\x00\x00\u07d4\xd9j\xc2Pt\t\u01e3\x83\xab.\xee\x18\"\xa5\xd78\xb3kV\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xd9m\xb3;{Z\x95\f>\xfa-\xc3\x1b\x10\xba\x10\xa52\uf1c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xd9wYe\xb7\x16Gfu\xa8\xd5\x13\xeb\x14\xbb\xf7\xb0|\xd1J\x8a\x01\x13.m-#\xc5\xe4\x00\x00\u07d4\xd9{\xc8J\xbdG\xc0[\xbfE{.\xf6Y\xd6\x1c\xa5\xe5\u43c9\x06\x9d\x17\x11\x9d\u0168\x00\x00\u07d4\xd9\u007fE&\u07a9\xb1c\xf8\xe8\xe3:k\u03d2\xfb\x90}\xe6\xec\x89\x0feJ\xafM\xb2\xf0\x00\x00\u07d4\xd9\u007f\xe6\xf5?*X\xf6\xd7mu*\xdft\xa8\xa2\xc1\x8e\x90t\x89\x10\xcd\xf9\xb6\x9aCW\x00\x00\u07d4\u0659\x99\xa2I\r\x94\x94\xa50\xca\xe4\xda\xf3\x85T\xf4\xddc>\x89\x06\x81U\xa46v\xe0\x00\x00\u07d4\u065d\xf7B\x1b\x93\x82\xe4,\x89\xb0\x06\xc7\xf0\x87p*\aW\xc0\x89\x1a\x05V\x90\xd9\u06c0\x00\x00\xe0\x94\u0677\x83\xd3\x1d2\xad\xc5\x0f\xa3\xea\u02a1]\x92\xb5h\xea\xebG\x8a\a3\xaf\x907L\x1b(\x00\x00\u07d4\xd9\xd3p\xfe\xc65v\xab\x15\xb3\x18\xbf\x9eX6M\u00a3U*\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xd9\xd4/\xd1>\xbdK\xf6\x9c\xac^\x9c~\x82H:\xb4m\xd7\xe9\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xd9\xe2~\xb0}\xfcq\xa7\x06\x06\f\u007f\a\x928\u0293\xe8\x859\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xd9\xe3\x85~\xfd\x1e *D\x17p\xa7w\xa4\x9d\xccE\xe2\xe0\u04c9\f\x1d\xaf\x81\u0623\xce\x00\x00\u07d4\xd9\xec.\xfe\x99\xff\\\xf0\r\x03\xa81{\x92\xa2J\xefD\x1f~\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xd9\xec\x8f\xe6\x9bw\x16\xc0\x86Z\xf8\x88\xa1\x1b+\x12\xf7 \xed3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xd9\xf1\xb2d\b\xf0\xecg\xad\x1d\ro\xe2.\x85\x15\xe1t\x06$\x89\x01M\x11 \u05f1`\x00\x00\u07d4\xd9\xf5G\xf2\xc1\xde\x0e\u064aS\xd1a\xdfWc]\xd2\x1a\x00\xbd\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xd9\xff\x11]\x01&l\x9fs\xb0c\xc1\xc28\xef5e\xe6;6\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xda\x06\x04N)<e,F\u007f\xe7AF\xbf\x18[!3\x8a\x1c\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\vH\xe4\x89\xd3\x02\xb4\xb7\xbf O\x95|\x1c\x9b\u30f0\u07c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xda\rK~\xf9\x1f\xb5Z\xd2e\xf2Q\x14 g\xf1\x03v\xce\u058a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xda\x10\x97\x8a9\xa4o\U0003b10c\xf6]\xd9\xc7u\t\xa6\xd7\x0e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xda\x16\xdd\\=\x1a'\x145\x8f\xe3u,\xaeS\u06eb+\u930a\x04\x1b\xad\x15^e\x12 \x00\x00\u07d4\xda!L\x02>#&\xffil\x0091h\xceF\xff\xac9\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda*\x14\xf9r@\x15\u05d0\x14\xed\x8eY\th\x1dYaH\xf1\x89\x02\xa1\x0f\x0f\x8a\x91\xab\x80\x00\u07d4\xda*\u054ew\xde\xdd\xed\xe2\x18vF\xc4e\x94Z\x8d\xc3\xf6A\x89#\xc7W\a+\x8d\xd0\x00\x00\u07d4\xda0\x17\xc1P\xdd\r\xce\u007f\u03c8\x1b\nH\xd0\xd1\xc7V\xc4\u01c9\x05k\xf9\x1b\x1ae\xeb\x00\x00\u07d4\xda4\xb2\xea\xe3\v\xaf\xe8\xda\xec\xcd\xe8\x19\xa7\x94\u0349\xe0\x95I\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdaJ_U\u007f;\xab9\n\x92\xf4\x9b\x9b\x90\n\xf3\fF\xae\x80\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdaPU7S\u007f\xfb3\xc4\x15\xfe\xc6Ni\xba\xe0\x90\xc5\xf6\x0f\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdai\x8dd\xc6\\\u007f+,rS\x05\x9c\xd3\u0441\u0619\xb6\xb7\x89\x10\x04\xe2\xe4_\xb7\xee\x00\x00\u07d4\xdaw2\xf0/.'.\xaf(\u07d7.\xcc\r\xde\xed\x9c\xf4\x98\x89\v \xbf\xbfig\x89\x00\x00\u07d4\xdaz\xd0%\xeb\xde%\xd2\"C\u02c3\x0e\xa1\xd3\xf6JVc#\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\u0685]SG\u007fP^\xc4\xc8\xd5\u8ed1\x80\u04c6\x81\x11\x9c\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\u0687^N/<\xab\xe4\xf3~\x0e\xae\xd7\xd1\xf6\xdc\xc6\xff\xefC\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u068b\xbe\xe1\x82\xe4U\xd2\t\x8a\xcb3\x8amE\xb4\xb1~\u0636\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0698.\x96C\xff\xec\xe7#\aZ@\xfewnZ\xce\x04\xb2\x9b\x89\b\xb8\xb6\u0259\x9b\xf2\x00\x00\u07d4\u069fUF\tF\u05ff\xb5p\xdd\xecu|\xa5w;XB\x9a\x89\x1b\x84]v\x9e\xb4H\x00\x00\u07d4\u06a1\xbdz\x91H\xfb\x86\\\xd6\x12\xdd5\xf1b\x86\x1d\x0f;\u0709\xa68\xabr\xd9,\x13\x80\x00\xe0\x94\u06a6<\xbd\xa4]\u0507\xa3\xf1\xcdJtj\x01\xbb^\x06\v\x90\x8a\x01\x04\x16\u0670*\x89$\x00\x00\u07d4\u06a7v\xa6uDi\u05f9&z\x89\xb8g%\xe7@\xda\x0f\xa0\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\u06ac\x91\xc1\xe8Y\xd5\xe5~\xd3\bKP \x0f\x97f\xe2\xc5+\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u06ac\xda\xf4\"&\xd1\\\xb1\u03d8\xfa\x15\x04\x8c\u007fL\xee\xfei\x89\x10CV\x1a\x88)0\x00\x00\xe0\x94\u06b6\xbc\u06c3\xcf$\xa0\xae\x1c\xb2\x1b;[\x83\xc2\xf3\x82I'\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\u06bb\b\x89\xfc\x04)&\xb0^\xf5{% \x91\n\xbcKAI\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06bc\"PB\xa6Y,\xfa\x13\xeb\xe5N\xfaA\x04\bx\xa5\xa2\x89\x0e\x11\xfa\xd5\xd8\\\xa3\x00\x00\u07d4\xda\xc0\xc1w\xf1\x1c\\>>x\xf2\xef\xd6c\xd12!H\x85t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xd16\xb8\x81x\xb4\x83zlx\x0f\xeb\xa2&\xb9\x85i\xa9L\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xda\xdb\xfa\xfd\x8bb\xb9*$\xef\xd7RV\u0743\xab\xdb\u05fb\u06c9\x01\x11du\x9f\xfb2\x00\x00\u07d4\xda\xdc\x00\xaby'`</\xcf1\xce\xe3R\xf8\x0elMcQ\x89lf\xe9\xa5Sx\xb8\x00\x00\u07d4\xda\xe0\xd3>\xaa4\x15i\xfa\x9f\xf5\x98&\x84\x85JJ2\x8an\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xda\xe7 \x1e\xab\x8c\x063\x02\x93\ri9)\xd0\u007f\x95\xe7\x19b\x89\x91\xae\xc0(\xb4\x19\x81\x00\x00\u07d4\xda\xed\u052d\x10{'\x1e\x89Hl\xbf\x80\xeb\xd6!\u0757Ex\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x04\xfa\xd9\u011f\x9e\x88\v\xeb\x8f\xcf\x1d:8\x90\u4cc4o\x89CZ\xe6\xcc\fX\xe5\x00\x00\u07d4\xdb\f\u01cft\u0642{\u070ads'n\xb8O\u0717b\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x12\x93\xa5\x06\xe9\f\xad*Y\xe1\xb8V\x1f^f\x96\x1ag\x88\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb\x19\xa3\x98\"06\x8f\x01w!\x9c\xb1\f\xb2Y\u0372%|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb#\xa6\xfe\xf1\xaf{X\x1ew,\xf9\x18\x82\u07b2Qo\xc0\xa7\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb$O\x97\xd9\xc4K\x15\x8a@\xed\x96\x06\xd9\xf7\xbd8\x9131\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xdb(\x8f\x80\xff\xe22\u00baG\u0314\xc7c\xcfo\u0278+\r\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xdb*\f\x9a\xb6M\xf5\x8d\u07f1\u06ec\xf8\xba\r\x89\xc8[1\xb4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdb4t^\u0785v\xb4\x99\xdb\x01\xbe\xb7\xc1\xec\u0685\xcfJ\xbe\x89\x04V9\x18$O@\x00\x00\u07d4\xdb?%\x8a\xb2\xa3\xc2\xcf3\x9cD\x99\xf7ZK\xd1\xd3G.\x9e\x89QP\xae\x84\xa8\xcd\xf0\x00\x00\u07d4\xdbK\xc8;\x0ek\xaa\xdb\x11V\xc5\xcf\x06\xe0\xf7!\x80\x8cR\u01c9/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xdbc\x12-\xe7\x03}\xa4\x97\x151\xfa\u9bc5\x86x\x86\u0192\x89\x0f\x04%\xb0d\x1f4\x00\x00\u07d4\xdbl*s\xda\xc7BJ\xb0\xd01\xb6ga\x12%f\xc0\x10C\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xdbnV\f\x9b\xc6 \u053e\xa3\xa9MG\xf7\x88\v\xf4\u007f-_\x89\x04\xda\x0f\xdf\xcf\x05v\x00\x00\u07d4\xdbo\xf7\x1b=\xb0\x92\x8f\x83\x9e\x05\xa72;\xfbW\u049c\x87\xaa\x891T\xc9r\x9d\x05x\x00\x00\u07d4\xdbsF\vY\xd8\xe8PE\xd5\xe7R\xe6%Y\x87^BP.\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xdbw\xb8\x8d\xcbq/\xd1~\xe9\x1a[\x94t\x8dr\f\x90\xa9\x94\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdb}@7\b\x1fle\xf9Gk\x06\x87\xd9\u007f\x1e\x04M\n\x1d\x89#\xc7W\a+\x8d\xd0\x00\x00\xe0\x94\u06c8.\xac\xed\xd0\xef\xf2cQ\x1b1*\u06fcY\u01b8\xb2[\x8a\x01\xedO\xdez\"6\xb0\x00\x00\u07d4\u06d3q\xb3\fL\x84NY\xe0>\x92K\xe6\x06\xa98\xd1\xd3\x10\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06e4ym\f\xebM:\x83k\x84\xc9o\x91\n\xfc\x10?[\xa0\x89\t\b\xf4\x93\xf77A\x00\x00\u07d4\u06ed\xc6\x1e\xd5\xf0F\n\u007f\x18\xe5\x1b/\xb2aM\x92d\xa0\xe0\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u06f6\xacH@'\x04\x16B\xbb\xfd\x8d\x80\xf9\xd0\xc1\xcf3\xc1\xeb\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u06fc\xbby\xbfG\x9aB\xadq\xdb\u02b7{Z\u07ea\x87,X\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xdb\xc1\xce\x0eI\xb1\xa7\x05\xd2. 7\xae\xc8x\xee\ru\xc7\x03\x89\r\x8drkqw\xa8\x00\x00\u07d4\xdb\xc1\xd0\xee+\xabS\x11@\xde\x13w\"\xcd6\xbd\xb4\xe4q\x94\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\u015e\u0609s\u07ad1\b\x84\":\xf4\x97c\xc0P0\xf1\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u0794\xdb\xc6ie\xe4&\xff\x1a\xc8z\xd6\xebx\xc1\xd9Rq\x15\x8f\x9f\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xdb\xcb\xcdzW\ua7724\x9b\x87\x8a\xf3K\x1a\xd6B\xa7\xf1\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdb\xd5\x1c\xdf,;\xfa\xcd\xff\x10b!\xde.\x19\xadmB\x04\x14\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdb\xd7\x1e\xfaK\x93\u0209\xe7e\x93\xde`\x9c;\x04\u02ef\xbe\b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xdb\xf5\xf0a\xa0\xf4\x8e^ia\x879\xa7}.\xc1\x97h\xd2\x01\x89\b=lz\xabc`\x00\x00\u07d4\xdb\xf8\xb19g\xf5Q%'-\xe0V%6\xc4P\xbaVU\xa0\x89n\xf5x\xf0n\f\xcb\x00\x00\u07d4\xdb\xfb\x1b\xb4d\xb8\xa5\x8eP\r.\xd8\u0797,E\xf5\xf1\xc0\xfb\x89V\xbcu\xe2\xd61\x00\x00\x00\xe0\x94\xdc\x06~\xd3\xe1-q\x1e\xd4u\xf5\x15n\xf7\xe7\x1a\x80\xd94\xb9\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xdc\b\u007f\x93\x90\xfb\x9e\x97j\xc2:\xb6\x89TJ\tB\xec !\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xdc\x1e\xb9\xb6\xe6CQ\xf5d$P\x96E\xf8>y\xee\xe7l\xf4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\x1f\x19ya_\b!@\xb8\xbbx\xc6{'\xa1\x94'\x13\xb1\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xdc#\xb2`\xfc\xc2n}\x10\xf4\xbd\x04J\xf7\x94W\x94`\xd9\u0689\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xdc)\x11\x97E\xd23s \xdaQ\xe1\x91\x00\xc9H\u0640\xb9\x15\x89\b\xacr0H\x9e\x80\x00\x00\u07d4\xdc-\x15\xa6\x9fk\xb3;$j\xef@E\aQ\xc2\xf6uj\u0489l4\x10\x80\xbd\x1f\xb0\x00\x00\u07d4\xdc=\xaeY\xed\x0f\xe1\x8bXQ\x1eo\xe2\xfbi\xb2\x19h\x94#\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xdc?\x0evr\xf7\x1f\xe7R[\xa3\v\x97U\x18: \xb9\x16j\x8a\x02\b\x9c\xf5{[>\x96\x80\x00\xe0\x94\xdcCE\u0581.\x87\n\xe9\fV\x8cg\xd2\xc5g\u03f4\xf0<\x8a\x01k5-\xa5\xe0\xed0\x00\x00\u07d4\xdcD'[\x17\x15\xba\xea\x1b\x03EsZ)\xacB\xc9\xf5\x1bO\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xdcF\xc13%\u034e\xdf\x020\xd0h\x89d\x86\xf0\a\xbfN\xf1\x89Hz\x9a0E9D\x00\x00\u07d4\xdcQ\xb2\u071d$z\x1d\x0e[\xc3l\xa3\x15oz\xf2\x1f\xf9\xf6\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdcS\x05\xb4\x02\n\x06\xb4\x9de||\xa3L5\xc9\x1c_,V\x8a\x01}\xf6\xc1\r\xbe\xba\x97\x00\x00\u07d4\xdcW4[8\xe0\xf0g\u0263\x1d\x9d\xea\xc5'Z\x10\x94\x93!\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdcWG}\xaf\xa4/p\\\u007f\xe4\x0e\xae\x9c\x81un\x02%\xf1\x89\x1b\x1b\x81(\xa7An\x00\x00\u07d4\xdc_Z\xd6c\xa6\xf2c2}d\xca\xc9\xcb\x13=,\x96\x05\x97\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdcp:_7\x94\xc8Ml\xb3TI\x18\xca\xe1J5\u00fdO\x89dI\xe8NG\xa8\xa8\x00\x00\xe0\x94\xdcs\x8f\xb2\x17\u03ad/iYL\b\x17\r\xe1\xaf\x10\xc4\x19\xe3\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xdcv\xe8[\xa5\v\x9b1\xec\x1e& \xbc\xe6\xe7\xc8\x05\x8c\x0e\xaf\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\u0703\xb6\xfd\rQ!1 G\a\xea\xf7.\xa0\xc8\u027e\xf9v\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u070c)\x12\xf0\x84\xa6\u0444\xaasc\x85\x13\u033c2n\x01\x02\x89F3\xbc6\xcb\xc2\xdc\x00\x00\u07d4\u0711\x1c\xf7\xdc]\u04016Vg\x05(\xe93\x8eg\x03G\x86\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0730;\xfal\x111#NV\xb7\xea|Or\x14\x87Tkz\x89Hz\x9a0E9D\x00\x00\xe0\x94\u0736M\xf47X\xc7\u03d7O\xa6`HO\xbbq\x8f\x8cg\xc1\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdc\xc5-\x8f\x8d\x9f\xc7B\xa8\xb8'g\xf0US\x87\xc5c\xef\xff\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xdc\xcb7\x0e\u058a\xa9\"(0C\xef|\xad\x1b\x9d@?\xc3J\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdc\u0324 E\xec>\x16P\x8b`?\xd96\xe7\xfd}\xe5\xf3j\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xdc\xd1\fU\xbb\x85OuD4\xf1!\x9c,\x9a\x98\xac\xe7\x9f\x03\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xdc\u057c\xa2\x00S\x95\xb6u\xfd\xe5\x03VY\xb2k\xfe\xfcI\xee\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xdc\u06fdN&\x04\xe4\x0e\x17\x10\xccg0(\x9d\xcc\xfa\u04c9-\x89\xf9]\xd2\xec'\xcc\xe0\x00\x00\u07d4\xdc\xe3\f1\xf3\xcafr\x1e\xcb!<\x80\x9a\xabV\x1d\x9bR\xe4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdc\xf39eS\x13\x80\x161h\xfc\x11\xf6~\x89\xc6\xf1\xbc\x17\x8a\x89\x12'v\x854\x06\xb0\x80\x00\u07d4\xdc\xf6\xb6W&n\x91\xa4\xda\xe6\x03=\xda\xc1S2\u074d+4\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xdc\xf9q\x9b\xe8|oFum\xb4\x89\x1d\xb9\xb6\x11\xd2F\x9cP\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xdc\xff\xf3\xe8\xd2<*4\xb5k\u0473\xbdE\u01d3tC\"9\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xdd\x04\xee\xe7N\v\xf3\f?\x8dl,\u007fR\xe0Q\x92\x10\u07d3\x89\x04V9\x18$O@\x00\x00\xe0\x94\xdd&\xb4)\xfdC\xd8N\xc1y\x82S$\xba\u057f\xb9\x16\xb3`\x8a\x01\x16\xbf\x95\xbc\x842\x98\x00\x00\u07d4\xdd*#:\xde\xdef\xfe\x11&\xd6\xc1h#\xb6*\x02\x1f\xed\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd+\u07e9\x17\xc1\xf3\x10\xe6\xfa5\xaa\x8a\xf1i9\xc23\xcd}\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xdd5\xcf\xdb\u02d93\x95Sz\xec\xc9\xf5\x90\x85\xa8\xd5\u0776\xf5\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xddG\x18\x9a>d9qg\xf0b\x0eHEe\xb7b\xbf\xbb\xf4\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xddM\xd6\xd3`3\xb0co\u030d\t8`\x9fM\xd6OJ\x86\x89\x03@\xaa\xd2\x1b;p\x00\x00\u07d4\xddO_\xa2\x11\x1d\xb6\x8fk\xde5\x89\xb60)9[i\xa9-\x89\b\x96=\xd8\xc2\xc5\xe0\x00\x00\xe0\x94\xddc\x04/%\xed2\x88J\xd2n:\xd9Y\xeb\x94\xea6\xbfg\x8a\x04\x84\xd7\xfd\xe7\u0553\xf0\x00\x00\u07d4\xdde\xf6\xe1qc\xb5\xd2\x03d\x1fQ\xcc{$\xb0\x0f\x02\xc8\xfb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xddl\x06!\x93\xea\xc2=/\xdb\xf9\x97\xd5\x06:4k\xb3\xb4p\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdd{\u0366Y$\xaa\xa4\x9b\x80\x98J\xe1su\x02X\xb9(G\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdd\u007f\xf4A\xbao\xfe6q\xf3\xc0\u06bb\xff\x18#\xa5\x043p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u0742T\x12\x1an\x94/\xc9\b(\xf2C\x1fQ\x1d\xad\u007f2\u6263\x9b)\xe1\xf3`\xe8\x00\x00\xe0\x94\u074a\xf9\xe7vR#\xf4DoD\xd3\xd5\t\x81\x9a==\xb4\x11\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\u0755\xdb\xe3\x0f\x1f\x18w\xc5\xddv\x84\xae\xef0*\xb6\x88Q\x92\x8a\x01\xc5\xd8\xd6\xeb>2P\x00\x00\xe0\x94\u0756|L_\x8a\xe4~&o\xb4\x16\xaa\u0456N\xe3\xe7\xe8\u00ca\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u075bHZ;\x1c\xd3:j\x9cb\xf1\xe5\xbe\xe9'\x01\x85m%\x89\f3\x83\xed\x03\x1b~\x80\x00\xe0\x94\u0763q\xe6\x00\xd3\x06\x88\xd4q\x0e\b\x8e\x02\xfd\xf2\xb9RM_\x8a\x01w\"J\xa8D\xc7 \x00\x00\u07d4\u0764\xed*X\xa8\xdd \xa72u4{X\rq\xb9[\xf9\x9a\x89\x15\xa1<\xc2\x01\xe4\xdc\x00\x00\xe0\x94\u0764\xff}\xe4\x91\u0187\xdfEt\xdd\x1b\x17\xff\x8f$k\xa3\u044a\x04&\x84\xa4\x1a\xbf\xd8@\x00\x00\u07d4\u076bkQ\xa9\x03\v@\xfb\x95\xcf\vt\x8a\x05\x9c$\x17\xbe\u01c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\u076bu\xfb/\xf9\xfe\u02c8\xf8\x94vh\x8e+\x00\xe3g\xeb\xf9\x8a\x04\x1b\xad\x15^e\x12 \x00\x00\xe0\x94\u076b\xf1<<\x8e\xa4\xe3\xd7=x\xecqz\xfa\xfaC\x0eTy\x8a\b\xcf#\xf9\t\xc0\xfa\x00\x00\x00\u07d4\u076c1*\x96UBj\x9c\f\x9e\xfa?\xd8%Y\xefE\x05\xbf\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\u076ck\xf4\xbb\xdd}Y}\x9chm\x06\x95Y;\xed\xcc\xc7\xfa\x89.\xe4IU\b\x98\xe4\x00\x00\xe0\x94\u077d+\x93,v;\xa5\xb1\xb7\xae;6.\xac>\x8d@\x12\x1a\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u077d\xdd\x1b\xbd8\xff\xad\xe00]0\xf0 (\xd9.\x9f:\xa8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u077e\xe6\xf0\x94\xea\xe64 \xb0\x03\xfbGW\x14*\xeal\xd0\xfd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdd\u059c[\x9b\xf5\xebZ9\xce\xe7\xc34\x1a\x12\r\x97?\xdb4\x89k\xc1K\x8f\x8e\x1b5\x00\x00\xe0\x94\xdd\xdd{\x9en\xab@\x9b\x92&:\xc2r\u0680\x1bfO\x8aW\x8ai\xe1\r\xe7fv\u0400\x00\x00\u07d4\xdd\xe6p\xd0\x169fuv\xa2-\xd0]2F\xd6\x1f\x06\xe0\x83\x89\x01s\x17\x90SM\xf2\x00\x00\xe0\x94\xdd\xe7zG@\xba\b\xe7\xf7?\xbe:\x16t\x91)1t.\xeb\x8a\x044\xfeMC\x82\xf1\u0500\x00\u07d4\xdd\xe8\xf0\xc3\x1bt\x15Q\x1d\xce\xd1\xcd}F2>K\xd1\"2\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xdd\xe9i\xae\xf3N\xa8z\u0099\xb7Y~)+J\x01U\u030a\x89\x102\xf2YJ\x01s\x80\x00\u07d4\xdd\xf0\xcc\xe1\xfe\x99m\x91v5\xf0\a\x12\xf4\x05 \x91\xdf\xf9\xea\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdd\xf3\xadv58\x10\xbej\x89\xd71\xb7\x87\xf6\xf1q\x88a+\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xdd\xf5\x81\n\x0e\xb2\xfb.22;\xb2\u0255\t\xab2\x0f$\xac\x8a\x03\xca\\f\u067cD0\x00\x00\xe0\x94\xdd\xf9\\\x1e\x99\xce/\x9fV\x98\x05|\x19\xd5\xc9@'\xeeJn\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u0794\xdd\xfa\xfd\xbc|\x90\xf12\x0eT\xb9\x8f7F\x17\xfb\xd0\x1d\x10\x9f\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xdd\xfc\xca\x13\xf94\xf0\u03fe#\x1d\xa109\xd7\x04u\xe6\xa1\u040968\"\x16`\xa5\xaa\x80\x00\u07d4\xde\x02~\xfb\xb3\x85\x03\"n\xd8q\t\x9c\xb3\v\xdb\x02\xaf\x135\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xde\x06\xd5\xeawzN\xb1G^`]\xbc\xbfCDN\x807\xea\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xde\a\xfb[zFN;\xa7\xfb\xe0\x9e\x9a\xcb'\x1a\xf53\x8cX\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xde\x11!\x82\x9c\x9a\b(@\x87\xa4?\xbd/\xc1\x14*23\xb4\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xde\x17kR\x84\xbc\xee:\x83\x8b\xa2Og\xfc|\xbfg\u05ce\xf6\x89\x02\t\xce\b\xc9b\xb0\x00\x00\u07d4\xde!\"\x93\xf8\xf1\xd21\xfa\x10\xe6\tG\rQ,\xb8\xff\xc5\x12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde0\xe4\x9eZ\xb3\x13!M/\x01\u072b\u0389@\xb8\x1b\x1cv\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xde3\xd7\b\xa3\xb8\x9e\x90\x9e\xafe;0\xfd\u00e5\xd5\u0334\xb3\x89\t\x9c\x88\"\x9f\xd4\xc2\x00\x00\u07d4\xde7B\x99\xc1\xd0}ySs\x85\x19\x0fD.\xf9\xca$\x06\x1f\x89\a?u\u0460\x85\xba\x00\x00\u07d4\xdeB\xfc\xd2L\xe4#\x93\x830CgY_\x06\x8f\fa\a@\x89\x02r*p\xf1\xa9\xa0\x00\x00\u07d4\xdeP\x86\x8e\xb7\xe3\xc7\x197\xecs\xfa\x89\u074b\x9e\xe1\rE\xaa\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdeU\xde\x04X\xf8P\xb3~Mx\xa6A\xdd.\xb2\u074f8\u0389\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde[\x00_\xe8\u06ae\x8d\x1f\x05\xde>\xda\x04 f\xc6\xc4i\x1c\x89;\xa1\x91\v\xf3A\xb0\x00\x00\u07d4\xdea-\a$\xe8N\xa4\xa7\xfe\xaa=!B\xbd^\xe8-2\x01\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdem61\x06\xccb8\xd2\xf0\x92\xf0\xf07!6\xd1\xcdP\u018a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xde}\xee\"\x0f\x04W\xa7\x18}V\xc1\xc4\x1f.\xb0\n\xc5`!\x89\"%\xf3\x9c\x85\x05*\x00\x00\u07d4\u0782\u030dJ\x1b\xb1\xd9CC\x92\x96[>\x80\xba\xd3\xc0=O\x89P\x18nu\u0797\xa6\x00\x00\u07d4\u0797\xf43\a\x00\xb4\x8cImC|\x91\xca\x1d\xe9\u0130\x1b\xa4\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\u07d4\u079e\xffLy\x88\x11\xd9h\xdc\xcbF\r\x9b\x06\x9c\xf3\x02x\xe0\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\u07b1\xbc4\xd8mJM\xde%\x80\u063e\xaf\aN\xb0\xe1\xa2D\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\u07b2I]j\xca{*j-\x13\x8bn\x1aB\xe2\xdc1\x1f\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\u07b9rTGL\r/Zyp\xdc\xdb/R\xfb\x10\x98\xb8\x96\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07b9\xa4\x9aC\x870 \xf0u\x91\x85\xe2\v\xbbL\U000c1ecf\x89\vx\xed\xb0\xbf.^\x00\x00\u07d4\u07bb\u0743\x1e\x0f \xaen7\x82R\xde\xcd\xf9/|\xf0\xc6X\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xc3\xee\xc2d\nu,Fn+~~\u616f\xe9\xacA\xf4\x89G\u0257SYk(\x80\x00\u07d4\xde\xc8#s\xad\xe8\xeb\xcf*\xcbo\x8b\xc2AM\u05eb\xb7\rw\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xde\u0221\xa8\x98\xf1\xb8\x95\xd80\x1f\xe6J\xb3\xad]\xe9A\xf6\x89\x89*\xb4\xf6~\x8as\x0f\x80\x00\u07d4\xde\u025e\x97/\xcaqwP\x8c\x8e\x1aG\xac\"\xd7h\xac\xab|\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xde\xd8w7\x84\a\xb9Nx\x1cN\xf4\xaf|\xfc[\xc2 \xb5\x16\x89\x141y\xd8i\x11\x02\x00\x00\u07d4\xde\xe9B\xd5\xca\xf5\xfa\xc1\x14!\xd8k\x01\vE\x8e\\9)\x90\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xde\xee&\x89\xfa\x90\x06\xb5\x9c\xf2\x85#}\xe5;:\u007f\xd0\x148\x89\x18ey\xf2\x9e %\x00\x00\u07d4\xde\xfd\xdf\u055b\x8d,\x15N\xec\xf5\xc7\xc1g\xbf\v\xa2\x90]>\x89\x05\x12\xcb^&GB\x00\x00\u07d4\xde\xfe\x91A\xf4pE\x99\x15\x9d{\"=\xe4+\xff\xd8\x04\x96\xb3\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xdf\t\x8f^N=\xff\xa5\x1a\xf27\xbd\xa8e,Os\ud726\x89\x1b6\xa6DJ>\x18\x00\x00\xe0\x94\xdf\r\ba{\xd2R\xa9\x11\u07cb\xd4\x1a9\xb8=\u07c0\x96s\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xdf\x0f\xf1\xf3\xd2z\x8e\xc9\xfb\x8fk\f\xb2T\xa6;\xba\x82$\xa5\x89\xec\xc5 )E\xd0\x02\x00\x00\u07d4\xdf\x1f\xa2\xe2\x0e1\x98^\xbe,\x0f\f\x93\xb5L\x0f\xb6z&K\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xdf!\x1c\xd2\x12\x88\xd6\xc5o\xaef\xc3\xffTb]\u0531T'\x89\x87\x86\xcdvN\x1f,\x00\x00\u07d4\xdf#k\xf6\xab\xf4\xf3)7\x95\xbf\f(q\x8f\x93\u3c73k\x89Hz\x9a0E9D\x00\x00\u07d4\xdf1\x02_VI\xd2\xc6\xee\xa4\x1e\u04fd\xd3G\x1ay\x0fu\x9a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xdf7\xc2.`:\xed\xb6\nbrS\xc4}\x8b\xa8f\xf6\xd9r\x8a\x05\x15\n\xe8J\x8c\xdf\x00\x00\x00\u07d4\xdf;r\u017dq\u0501N\x88\xa6#!\xa9=@\x11\xe3W\x8b\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf?W\xb8\xeed4\xd0G\"=\xeft\xb2\x0fc\xf9\xe4\xf9U\x89\r\x94b\xc6\xcbKZ\x00\x00\u07d4\xdfD\xc4\u007f\xc3\x03\xacv\xe7O\x97\x19L\xcag\xb5\xbb<\x02?\x89 \t\xc5\u023fo\xdc\x00\x00\u07d4\xdfG\xa6\x1brSQ\x93\xc5a\xcc\xccu\xc3\xf3\xce\b\x04\xa2\x0e\x89\x15\x93\\\vN=x\x00\x00\u07d4\xdfG\xa8\xef\x95\xf2\xf4\x9f\x8eoX\x18AT\x14]\x11\xf7'\x97\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xdfS\x003F\xd6\\^zdk\xc04\xf2\xb7\xd3/\xcb\xe5j\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfW5:\xaf\xf2\xaa\xdb\n\x04\xf9\x01N\x8d\xa7\x88N\x86X\x9c\x89\bH\x86\xa6nO\xb0\x00\x00\u07d4\xdf`\xf1\x8c\x81*\x11\xedN'v\xe7\xa8\x0e\xcf^S\x05\xb3\u05890\xca\x02O\x98{\x90\x00\x00\u07d4\xdfd\x85\xc4)z\xc1R\xb2\x89\xb1\x9d\xde2\xc7~\xc4\x17\xf4}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xdff\n\x91\u06b9\xf70\xf6\x19\rP\xc89\x05aP\aV\u0289lk\x93[\x8b\xbd@\x00\x00\u07d4\xdfn\xd6\x00jj\xbe\x88n\xd3=\x95\xa4\xde(\xfc\x12\x189'\x891T\xc9r\x9d\x05x\x00\x00\u07d4\u07c5\x10y>\xee\x81\x1c-\xab\x1c\x93\xc6\xf4G?0\xfb\xef[\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u07cdH\xb1\xeb\a\xb3\xc2\x17y\x0el-\xf0M\xc3\x19\xe7\xe8H\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\u07e6\xb8\xb8\xad1\x84\xe3W\xda()Q\u05d1a\u03f0\x89\xbc\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\u07ef1\xe6\"\xc0=\x9e\x18\xa0\u0778\xbe`\xfb\xe3\xe6a\xbe\n\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\u07f1bn\xf4\x8a\x1d}uR\xa5\xe0)\x8f\x1f\xc2:;H-\x89\\\xe8\x95\u0754\x9e\xfa\x00\x00\xe0\x94\u07f4\u052d\xe5/\u0301\x8a\xccz,k\xb2\xb0\x02$e\x8fx\x8a\x01\xa4 \xdb\x02\xbd}X\x00\x00\u07d4\u07fdB2\xc1|@z\x98\r\xb8\u007f\xfb\u036060\xe5\xc4Y\x89\x1d\xfc\u007f\x92I#S\x00\x00\u07d4\xdf\xcb\xdf\tEN\x1a^J@\xd3\xee\xf7\xc5\xcf\x1c\xd3\u0794\x86\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xdf\xdb\xce\xc1\x01K\x96\xda!X\xcaQ>\x9c\x8d;\x9a\xf1\xc3\u0409lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xdf\xde\xd2WK'\xd1a:}\x98\xb7\x15\x15\x9b\r\x00\xba\xab(\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xdf\xdfC9<d\x9c\xae\xbe\x1b\xb1\x80Y\xde\xcb9\xf0\x9f\xb4\xe8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xdf\xe3\xc5*\x92\xc3\x03\x96\xa4\xe3:P\x17\r\xc9\x00\xfc\xf8\xc9\u03c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xdf\xe5I\xfe\x840\xe5R\xc6\xd0|\u00f9,\xcdC\xb1/\xb5\x0f\x89\x04\x88u\xea\xf6V*\x00\x00\u07d4\xdf\xe9)\xa6\x1c\x1b8\xed\xdb\xe8,%\xc2\xd6u<\xb1\xe1-h\x89\x15\xd1\xcfAv\xae\xba\x00\x00\u07d4\xdf\xf1\xb2 \xde=\x8e\x9c\xa4\xc1\xb5\xbe4\xa7\x99\xbc\xde\xd4\xf6\x1c\x89\x14\xe4\xe3S\xea9B\x00\x00\xe0\x94\xdf\xf4\x00y1xe\x93\xb2)\xef\u555f:N!\x9eQ\xaf\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4\xdf\xfc\xeaT!\xec\x15\x90\fn\xcf\xc7w\x18N\x14\x0e \x9e$\x89\x01\x15G8$4N\x00\x00\u07d4\xe0\x01\xab\xa7|\x02\xe1r\bl\x19P\xff\xfb\u02a3\v\x83H\x8f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe0\x04\x84x\x8d\xb5\x0f\u01a4\x8e7\x9d\x12>P\x8b\x0fnZ\xb1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe0\x06\x04b\xc4\u007f\xf9g\x9b\xae\xf0qY\xca\xe0\x8c)\xf2t\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\r\x15;\x106\x91C\xf9\u007fT\xb8\xd4\xca\"\x9e\xb3\xe8\xf3$\x89\b=lz\xabc`\x00\x00\u07d4\xe0\x12\xdbE8'\xa5\x8e\x16\xc16V\b\xd3n\xd6Xr\x05\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x15G\xbaB\xfc\xaf\xaf\x93\x93\x8b\xec\xf7i\x9ft)\n\xf7O\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x16\xdc\x13\x8e%\x81[\x90\xbe?\xe9\xee\xe8\xff\xb2\xe1\x05bO\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\x18Y\xf2B\xf1\xa0\xec`/\xa8\xa3\xb0\xb5v@\xec\x89\a^\x89\x1e\x16,\x17{\xe5\xcc\x00\x00\xe0\x94\xe0 \xe8cb\xb4\x87u(6\xa6\xde\v\xc0,\xd8\u061a\x8bj\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xe0#\xf0\x9b(\x87a,|\x9c\xf1\x98\x8e::`+3\x94\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0'\"\x13\xe8\xd2\xfd>\x96\xbdb\x17\xb2KK\xa0\x1bapy\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0+t\xa4v(\xbe1[\x1fv\xb3\x15\x05J\xd4J\xe9qo\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe02 \u0197\xbc\u048f&\xef\vt@J\x8b\xeb\x06\xb2\xba{\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe05/\u07c1\x9b\xa2e\xf1L\x06\xa61\\J\xc1\xfe\x13\x1b.\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe08\x8a\xed\xdd?\xe2\xadV\xf8WH\xe8\x0eq\n4\xb7\xc9.\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0<\x00\xd0\x03\x88\xec\xbfO&=\n\xc7x\xbbA\xa5z@\u064966\xc9yd6t\x00\x00\u07d4\xe0I \xdcn\xcc\x1dn\xcc\bO\x88\xaa\n\xf5\u06d7\xbf\x89:\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xe0Ir\xa8<\xa4\x11+\xc8q\xc7-J\xe1al/\a(\u06c9\x0e\x81\xc7\u007f)\xa3/\x00\x00\u07d4\xe0O\xf5\xe5\xa7\u2bd9]\x88W\xce\x02\x90\xb5:+\x0e\xda]\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xe0P)\xac\xeb\axg[\xef\x17A\xab,\u0493\x1e\xf7\xc8K\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0V\xbf?\xf4\x1c&%o\xefQqf\x12\xb9\u04da\u0799\x9c\x89\x05k\xe7W\xa1.\n\x80\x00\u07d4\xe0a\xa4\xf2\xfcw\xb2\x96\u045a\xda#\x8eI\xa5\u02ce\xcb\xfap\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0f>\x8c\xd6g\x92\xa6A\xf5nP\x03f\x01G\x88\x0f\x01\x8e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0f\x8f\xa8,\x14\xd6\xe8\xd9:S\x11>\xf2\x86/\xa8\x15\x81\xbc\x89//9\xfclT\x00\x00\x00\u07d4\xe0i\xc0\x173R\xb1\v\xf6\x83G\x19\xdb[\xed\x01\xad\xf9{\xbc\x89\x01\x064\xf8\xe52;\x00\x00\u07d4\xe0l)\xa8\x15\x17\xe0\u0507\xb6\u007f\xb0\xb6\xaa\xbcOW6\x83\x88\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xe0l\xb6)G\x04\xee\xa7C|/\xc3\xd3\as\xb7\xbf8\x88\x9a\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\u07d4\xe0q7\xae\r\x11m\x0353\xc4\uad16\xf8\xa9\xfb\tV\x9c\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe0v\xdb0\xabHoy\x19N\xbb\xc4]\x8f\xab\x9a\x92B\xf6T\x8a\x01\x06`~4\x94\xba\xa0\x00\x00\u07d4\xe0~\xbb\xc7\xf4\xdaAnB\xc8\xd4\xf8B\xab\xa1b3\xc1%\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x81\xca\x1fH\x82\xdb`C\u0569\x19\a\x03\xfd\xe0\xab;\xf5m\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x83\xd3Hc\xe0\xe1\u007f\x92ky(\xed\xff1~\x99\x8e\x9cK\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\x8b\x9a\xbak\xd9\u048b\xc2\x05gy\xd2\xfb\xf0\xf2\x85Z=\x9d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\x8b\u009c+H\xb1i\xff+\xdc\x16qLXnl\xb8\\\u03c9\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe0\x8c`11\x06\xe3\xf93O\xe6\xf7\xe7bM!\x110\xc0w\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xe0\x9ch\xe6\x19\x98\xd9\xc8\x1b\x14\xe4\xee\x80+\xa7\xad\xf6\xd7L\u06c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe0\x9f\xeauZ\xee\x1aD\xc0\xa8\x9f\x03\xb5\u07b7b\xba3\x00o\x89;\xa2\x89\xbc\x94O\xf7\x00\x00\xe0\x94\xe0\xa2T\xac\t\xb9r[\xeb\xc8\xe4`C\x1d\xd0s.\xbc\xab\xbf\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe0\xaai6UU\xb7?(#3\xd1\xe3\f\x1b\xbd\a(T\xe8\x8a\x01{x\x83\xc0i\x16`\x00\x00\u07d4\xe0\xba\u064e\ue598\xdb\xf6\xd7`\x85\xb7\x92=\xe5uN\x90m\x89\t\r\x97/22<\x00\x00\u07d4\xe0\u012b\x90r\xb4\xe6\xe3eJI\xf8\xa8\xdb\x02jK3\x86\xa9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\u0380\xa4a\xb6H\xa5\x01\xfd\v\x82F\x90\u0206\x8b\x0eM\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe0\xcfi\x8a\x053'\xeb\xd1k}w\x00\t/\xe2\xe8T$F\x89\x05*4\u02f6\x1fW\x80\x00\xe0\x94\xe0\xd21\xe1D\xec\x91\a8l|\x9b\x02\xf1p,\xea\xa4\xf7\x00\x8a\x01\x0f\r\xba\xe6\x10\tR\x80\x00\u07d4\xe0\xd7kqf\xb1\xf3\xa1+@\x91\xee+)\u078c\xaa}\a\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe0\xe0\xb2\xe2\x9d\xdes\xafu\x98~\xe4Dl\x82\x9a\x18\x9c\x95\xbc\x89\b\x13\xcaV\x90m4\x00\x00\xe0\x94\xe0\xe9xu=\x98/\u007f\x9d\x1d#\x8a\x18\xbdH\x89\xae\xfeE\x1b\x8a\x02\r\u058a\xaf2\x89\x10\x00\x00\u07d4\xe0\xf3r4|\x96\xb5_}C\x06\x03K\xeb\x83&o\xd9\tf\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe0\xf9\x03\xc1\xe4\x8a\xc4!\xabHR\x8f=J&H\b\x0f\xe0C\x897\b\xba\xed=h\x90\x00\x00\u07d4\xe0\xff\v\xd9\x15D9\u0125\xb7#>)\x1d}\x86\x8a\xf5?3\x89\x15y!jQ\xbb\xfb\x00\x00\xe0\x94\xe1\n\xc1\x9cTo\xc2T|a\xc19\xf5\xd1\xf4Zff\u0570\x8a\x01\x02\xdao\xd0\xf7:<\x00\x00\xe0\x94\xe1\fT\x00\x88\x11?\xa6\xec\x00\xb4\xb2\u0202O\x87\x96\xe9n\u010a2\x0fE\t\xab\x1e\xc7\xc0\x00\x00\xe0\x94\xe1\x17:$})\xd8#\x8d\xf0\x92/M\xf2Z\x05\xf2\xafw\u00ca\bx\xc9]V\x0f0G\x80\x00\xe0\x94\xe1 >\xb3\xa7#\xe9\x9c\" \x11|\xa6\xaf\xebf\xfaBOa\x8a\x02\x00\uf49e2V\xfe\x00\x00\xe0\x94\xe11\xf8~\xfc^\xf0~C\xf0\xf2\xf4\xa7G\xb5Q\xd7P\xd9\xe6\x8a\x04<%\xe0\xdc\xc1\xbd\x1c\x00\x00\u07d4\xe13N\x99\x83y\xdf\xe9\x83\x17pby\x1b\x90\xf8\x0e\xe2-\x8d\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe15@\xec\xee\x11\xb2\x12\xe8\xb7u\u070eq\xf3t\xaa\xe9\xb3\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1;=+\xbf\u073c\x87r\xa23\x15rL\x14%\x16|V\x88\x897\xf3y\x14\x1e\xd0K\x80\x00\u07d4\xe1D=\xbd\x95\xccA#\u007fa:HEi\x88\xa0Oh2\x82\x89\xd8\xd8X?\xa2\xd5/\x00\x00\u07d4\xe1F\x17\xf6\x02%\x01\xe9~{>-\x886\xaaa\xf0\xff-\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe1I\xb5rl\xafm^\xb5\xbf*\xccA\xd4\xe2\xdc2\x8d\u1089i*\xe8\x89p\x81\xd0\x00\x00\xe0\x94\xe1T\xda\xea\xdbTX8\xcb\u01aa\fUu\x19\x02\xf5(h*\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\u07d4\xe1l\xe3Ya\xcdt\xbdY\r\x04\u012dJ\x19\x89\xe0V\x91\u0189\a\xea(2uw\b\x00\x00\u07d4\xe1r\xdf\xc8\xf8\f\xd1\xf8\u03459\xdc&\b \x14\xf5\xa8\xe3\u8262\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xe1w\xe0\xc2\x01\xd35\xba9V\x92\x9cW\x15\x88\xb5\x1cR#\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1x\x12\xf6l^e\x94\x1e\x18lF\x92+n{/\x0e\xebF\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe1\x80\u079e\x86\xf5{\xaf\xac\u05d0O\x98&\xb6\xb4\xb2c7\xa3\x89-\x04\x1dpZ,`\x00\x00\xe0\x94\xe1\x92H\x9b\x85\xa9\x82\xc1\x882F\xd9\x15\xb2)\xcb\x13 \u007f8\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xe1\x95<n\x97X\x14\xc5q1\x1c4\xc0\xf6\xa9\x9c\xdfH\xab\x82\x89\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xe1\xae\x02\x9b\x17\xe3s\xcd\xe3\xdeZ\x91R \x1a\x14\xca\xc4\xe1\x19\x89\x05kU\xaeX\xca@\x00\x00\u07d4\u1cac\xa1T\xb8\xe0vlN\xba0\xbc\x10\xc7\xf3P6\xf3h\x89\x01\x15G8$4N\x00\x00\u07d4\u1cdb\x88\u0650\r\xbcJl\xdcH\x1e\x10`\b\n\x8a\xec<\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe1\xb62\x01\xfa\xe1\xf1)\xf9\\z\x11k\xd9\xdd\xe5\x15\x9cl\u068a\x04\xd6\x05s\xa2\xf0\xc9\xef\x00\x00\u07d4\u1feaZE\xc5\x04B\x89#\u0126\x11\x92\xa5[\x14\x00\xb4]\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xe1\xc6\a\xc0\xa8\xa0`\u068f\x02\xa8\xeb8\xa0\x13\xea\x8c\xda[\x8c\x89+\xa3\x9e\x82\xed]t\x00\x00\xe0\x94\xe1\u02c3\xec^\xb6\xf1\xee\xb8^\x99\xb2\xfcc\x81/\u0795q\x84\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe1\xd9\x1b\tT\xce\xde\"\x1do$\u01d8_\u0159e\xfb\x98\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe1\u07f5\u0309\x0e\u8c87~\x88]&|%a\x87\xd0\x19\xe6\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe1\xe8\xc5\v\x80\xa3R\xb2@\xcesB\xbb\xfd\xf5i\f\xc8\xcb\x14\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xe1\xf6>\xbb\xc6,{tD\x04\x0e\xb9\x96#\x96Ovg\xb3v\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\x06\xfbs$\xe9\u07b7\x9e\x19\x904\x96\u0596\x1b\x9b\xe5f\x03\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe2\aW\x8e\x1fM\u06cf\xf6\u0546{9X-q\xb9\x81*\u0149\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xe2\b\x81*h@\x98\xf3\xdaN\xfej\xba%bV\xad\xfe?\xe6\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2\tT\xd0\xf4\x10\x8c\x82\xd4\u0732\x14\x8d&\xbb\xd9$\xf6\xdd$\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe2\v\xb9\xf3\x96d\x19\xe1K\xbb\xaa\xaag\x89\xe9$\x96\u03e4y\x89\xbb\xd8%\x03\aRv\x00\x00\u07d4\xe2\r\x1b\xcbq(m\xc7\x12\x8a\x9f\xc7\xc6\xed\u007fs8\x92\xee\xf5\x896d\xf8\xe7\xc2J\xf4\x00\x00\u0794\xe2\x19\x12\x15\x98?3\xfd3\xe2,\u0522I\x00T\xdaS\xfd\u0708\xdbD\xe0I\xbb,\x00\x00\u07d4\xe2\x19\x8c\x8c\xa1\xb3\x99\xf7R\x15a\xfdS\x84\xa7\x13/\xbaHk\x897\b\xba\xed=h\x90\x00\x00\xe0\x94\xe2\x1cw\x8e\xf2\xa0\xd7\xf7Q\xea\x8c\aM\x1f\x81\"C\x86>N\x8a\x01\x1f\xc7\x0e,\x8c\x8a\xe1\x80\x00\xe0\x94\xe2)\xe7F\xa8?,\xe2S\xb0\xb0>\xb1G$\x11\xb5~W\x00\x8a\x016\x9f\xb9a(\xacH\x00\x00\u07d4\xe2+ \xc7x\x94F;\xafwL\xc2V\u057d\u06ff}\xdd\t\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe20\xfe\x1b\xff\x03\x18m\x02\x19\xf1]LH\x1b}Y\xbe(j\x89\x01\xfdt\x1e\x80\x88\x97\x00\x00\u07d4\xe27\xba\xa4\xdb\u0252n2\xa3\xd8]\x12d@-T\xdb\x01/\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2A\t\xbe/Q=\x87I\x8e\x92j(d\x99uO\x9e\u051e\x890\x0e\xa8\xad\x1f'\xca\x00\x00\u07d4\xe2Fh<\u025d\xb7\u0125+\u02ec\xaa\xb0\xb3/k\xfc\x93\u05c9lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe2Z\x16{\x03\x1e\x84am\x0f\x01?1\xbd\xa9]\xcccP\xb9\x8a\x02<upr\xb8\xdd\x00\x00\x00\u07d4\xe2[\x9fv\xb8\xad\x02?\x05~\xb1\x1a\xd9BW\xa0\x86.N\x8c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2fW\xf0\xed \x1e\xa29,\x92\"\xb8\np\x03`\x8d\xdf0\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xe2k\xf3\"wN\x18(\x87i\xd6~1\a\u07b7Dw\a\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2r\x8a>\x8c*\xaa\u0243\xd0]\u0187st\xa8\xf4F\xee\xe9\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xe2\x8b\x06\"Y\xe9n\xeb<\x8dA\x04\x94?\x9e\xb3%\x89<\xf5\x89Hz\x9a0E9D\x00\x00\xe0\x94\u237c\x8e\xfd^Ajv.\xc0\xe0\x18\x86K\xb9\xaa\x83({\x8a\x051\xf2\x00\xab>\x03\n\x80\x00\u07d4\xe2\x90K\x1a\xef\xa0V9\x8bb4\xcb5\x81\x12\x88\xd76\xdbg\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\u274a\xe4R\xdc\xf3\xb6\xacd^c\x04\t8UQ\xfa\xae\n\x89\x04Z\r\xa4\xad\xf5B\x00\x00\u07d4\xe2\xbb\xf8FA\xe3T\x1fl3\xe6\xedh:cZp\xbd\xe2\xec\x89\x1bA<\xfc\xbfY\xb7\x80\x00\u07d4\xe2\xcf6\n\xa22\x9e\xb7\x9d+\xf7\xca\x04\xa2z\x17\xc52\xe4\u0609\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xe2\xdf#\xf6\xea\x04\xbe\xcfJ\xb7\x01t\x8d\xc0\x961\x84U\\\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xe2\xe1\\`\xdd8\x1e:K\xe2Pq\xab$\x9aL\\Rd\u0689\u007fk\u011b\x81\xb57\x00\x00\u07d4\xe2\xe2nN\x1d\xcf0\xd0H\xccn\u03ddQ\xec\x12\x05\xa4\xe9&\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xe2\xeei\x1f#~\xe6R\x9beW\xf2\xfc\xdd=\xcf\fY\xecc\x8a\x01'r\x9c\x14h| \x00\x00\u07d4\xe2\xef\xa5\xfc\xa7\x958\xce`h\xbf1\xd2\xc5\x16\xd4\xd5<\b\xe5\x89\a\x1c\xc4\b\xdfc@\x00\x00\xe0\x94\xe2\xef\u0429\xbc@~\xce\x03\xd6~\x8e\xc8\xe9\u0483\xf4\x8d*I\x8a\x02\x99\xb3;\xf9\u0144\xe0\x00\x00\u07d4\xe2\xf4\r5\x8f^?\xe7F>\xc7\x04\x80\xbd.\u04d8\xa7\x06;\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe2\xf98=X\x10\xea{C\x18+\x87\x04\xb6+'\xf5\x92]9\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe2\xff\x9e\xe4\xb6\xec\xc1AA\xcct\xcaR\xa9\xe7\xa2\xee\x14\xd9\b\x89K\xe4\xe7&{j\xe0\x00\x00\xe0\x94\xe3\x02\x12\xb2\x01\x1b\xb5k\xdb\xf1\xbc5i\x0f:N\x0f\xd9\x05\xea\x8a\x01\xb2\u07dd!\x9fW\x98\x00\x00\u07d4\xe3\x03\x16\u007f=I`\xfe\x88\x1b2\x80\n+J\xef\xf1\xb0\x88\u0509lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\x04\xa3/\x05\xa87btJ\x95B\x97o\xf9\xb7#\xfa1\xea\x89Ur\xf2@\xa3F \x00\x00\u07d4\xe3\bCR\x04y7d\xf5\xfc\xbee\xebQ\x0fZtJeZ\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3\t\x97L\xe3\x9d`\xaa\xdf.ig2Q\xbf\x0e\x04v\n\x10\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xe3\x1bN\xef\x18L$\xab\t\x8e6\xc8\x02qK\xd4t=\xd0\u0509\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3!\xbbJ\x94j\xda\xfd\xad\xe4W\x1f\xb1\\\x00C\u04de\xe3_\x89Udu8+L\x9e\x00\x00\u07d4\xe3&<\xe8\xafm\xb3\xe4gXE\x02\xedq\t\x12^\xae\"\xa5\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3+\x1cG%\xa1\x87TI\u93d7\x0e\xb3\xe5@b\xd1X\x00\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe3/\x95vmW\xb5\xcdK\x172\x89\u0587o\x9edU\x81\x94\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe38@\u063c\xa7\u0698\xa6\xf3\u0416\xd8=\xe7\x8bp\xb7\x1e\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe38\xe8Y\xfe.\x8c\x15UHH\xb7\\\xae\u0368w\xa0\xe82\x89a\xac\xff\x81\xa7\x8a\xd4\x00\x00\u07d4\xe3=\x98\x02 \xfa\xb2Y\xafj\x1fK8\xcf\x0e\xf3\xc6\xe2\xea\x1a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe3=\xf4\u0380\u0336*v\xb1+\xcd\xfc\xec\xc4b\x89\x97:\xa9\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xe3?\xf9\x87T\x1d\xde\\\xde\u0a29m\xcc?3\xc3\xf2L\u008a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xe3A\v\xb7U|\xf9\x1dy\xfai\xd0\xdf\xea\n\xa0u@&Q\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3Ad-@\u04af\xce.\x91\a\xc6py\xacz&`\bl\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe3TS\xee\xf2\xcc<z\x04M\n\xc14\xbaaY\b\xfa\x82\xee\x89\a\xff\x1c\xcbua\xdf\x00\x00\u07d4\xe3j\x8e\xa8\u007f\x1e\x99\xe8\xa2\xdc\x1b&\b\xd1ff|\x9d\xfa\x01\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe3q'\x01a\x9c\xa7b<U\xdb:\n\xd3\x0e\x86}\xb0\x16\x8b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xe3\u007f_\xdcn\xc9}/\x86j\x1c\xfd\r:M\xa48{\"\xb5\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u31cf\x91\u0286\x05?\xce\xd5DF\x86\xa30\xe0\x9c\u00c8\xfb\x89\n\x84Jt$\xd9\xc8\x00\x00\u07d4\u32d1\xb3Q\x90\xb6\xd9\xde\xed\x02\x1c0\xaf\tK\x95?\u072a\x89\x01\u03afy[k\x86\x00\x00\u07d4\xe3\x8e\xf2\x8a^\u0644\xa7\xdb$\xa1\xaex-\xfb\x87\xf3\x97\xdf\u0189\a\xc0\x86\x0eZ\x80\xdc\x00\x00\u07d4\xe3\x92U\t\xc8\u0432\xa6s\x8c_jr\xf3S\x14I\x12H\u03896\xe9\xa8f\x9aDv\x80\x00\xe0\x94\xe3\x93=a\xb7}\xcd\xc7\x16@\u007f\x82P\xbc\x91\xe4\xff\xae\xb0\x9d\x8a\x12V\x98l\x95\x89\x1c \x00\x00\u07d4\xe3\x95\x1d\xe5\xae\xfa\xf0E\x87h\xd7t\xc2T\xf7\x15w5\xe5\x05\x89V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4\xe3\x99\xc8\x1a\x1dp\x1bD\xf0\xb6o3\x99\xe6k'Z\xaa\xf8\xc1\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe3\x9b\x11\xa8\xab\x1f\xf5\xe2.Z\xe6Qr\x14\xf7<[\x9bU\u0709lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe3\x9eF\xe1]\"\xceV\xe0\xc3/\x18w\xb7\u0462d\u03d4\xf3\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe3\xa4b\x1bf\x00E\x88\xe3\x12\x06\xf7\x18\xcb\x00\xa3\x19\x88\x9c\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\xa4\xf8<9\xf8Z\xf9\u0231\xb3\x12\xbf\xe5\xfc4#\xaf\xa64\x89\x01\x8d\x99?4\xae\xf1\x00\x00\u07d4\u3a1a\x19'\xccN-C\xfb\u0361\xe4\x14\xd3$\xa7\xd9\xe0W\x89\v#\xe2\xa96\xde\xc6\x00\x00\u07d4\xe3\xab<\xa9\xb8p\xe3\xf5HQs\x06\xbb\xa4\xde%\x91\xaf\xaf\u0089A\x0e4\xae\u030c\xd3\x00\x00\u07d4\xe3\xb3\xd2\u027fW\v\xe6\xa2\xf7*\u0721\x86,1\t6\xa4<\x89\x05m*\xa3\xa5\xc0\x9a\x00\x00\u07d4\xe3\xc0\xc1(2z\x9a\xd8\x01H\x13\x9e&\x97sB\x8ec\x8c\xb0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\xc8\x12sz\xc6\x06\xba\xf7R*\xd8\x17B\x8a6\x05\x0ez4\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xe3\xcf\xfe#\x9cd\xe7\xe2\x03\x88\xe6\"\x11s\x910\x1b)\x86\x96\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe3\xd3\ua899\x88xeV\x9e\x88\xbe!\x9b\xe5\a\x18\x9b\xe1\u0249\x18\xbao\xa9.\x93\x16\x00\x00\u07d4\xe3\u063fN\xfe\x84\xb1am\x1b\x89\xe4'\xdd\xc6\u0203\x06\x85\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe3\xd9\x15\xed\xa3\xb8%\xd6\xeeJ\xf92\x8d2\xac\x18\xad\xa3T\x97\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xe3\xdaO2@\x84L\x9bc#\xb4\x99i! q\"EC\x99\x8a\x02q\x90\xa9R\xdfK\xe5\x80\x00\u07d4\xe3\xeb,\n\x13*ROr\xcc\xc0\xd6\x0f\xee\x8bAh]9\xe2\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe3\xec\x18\xa7N\xd48U@\x9a&\xad\xe7\x83\r\xe8\xe4&\x85\xef\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xe3\xec\xe1\xf62q\x1d\x13\xbf\xff\xa1\xf8\xf6\x84\bq\xeeX\xfb'\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe3\xf8\v@\xfb\x83\xfb\x97\xbb\rR0\xafOn\u055b\x1c|\u0209Hz\x9a0E9D\x00\x00\u07d4\xe3\xff\xb0,\xb7\xd9\xeaRCp\x16\x89\xaf\xd5\xd4\x17\xd7\xed.\u0389\x04:w\xaa\xbd\x00x\x00\x00\u07d4\xe4\x00\xd6Q\xbb?-#\xd5\xf8I\xe6\xf9-\x9cW\x95\xc4:\x8a\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xe4\x06\xf5\xddr\u02b6m\x8an\xcb\u05bf\xb4\x94\xa7\xb6\xb0\x9a\xfe\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xe4\b\xaa\x99\x83S\a\ue926\xc5\xeb\x80\x1f\xe6\x94\x11\u007fp}\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xe4\b\xfc\ua879\x8f<d\x0fH\xfc\xba9\xf0V\x06mc\b\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe4\n|\x82\xe1WT\n\v\x00\x90\x1d\xbb\x86\xc7\x16\xe1\xa0b\u0689\x02\xb3\x1d$%\xf6t\x00\x00\u07d4\xe4\x1a\xea%\v\x87}B:c\xba+\xce/:a\xc0$\x8dV\x89\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xe40\xc0\x02O\xdb\xf7:\x82\xe2\x1f\xcc\xf8\xcb\u04118B\x1c!\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe42I\x12\xd6N\xa3\xae\xf7k</\xf9\u07c2\xc7\xe1:\u9449lk\x93[\x8b\xbd@\x00\x00\u07d4\xe46\x8b\xc1B\v5\xef\u0695\xfa\xfb\xc70\x90R\x19\x16\xaa4\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe47\xac\xbe\x0fb'\xb0\xe3o6\xe4\xbc\xf7\xcfa35\xfbh\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xe4Krd\u0743k\ue387\x97\x03@\xed+\x9a\xed\x8e\u0425\x8a\x018\xe7\xfa\xa0\x1a\x80:\x00\x00\u07d4\xe4N\xa5\x10c@QT\xaa\xe76\xbe+\xf1\xee;\x9b\xe69\xae\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe4bU\x01\xf5+z\xf5+\x19\xeda.\x9dT\xfd\xd0\x06\xb4\x92\x89\vZ\x90ZV\xdd\xd0\x00\x00\u07d4\xe4qYV\xf5/\x150n\xe9Pk\xf8+\xcc\xc4\x06\xb3\x89^\x89\x0e\xe7\x9dOH\xc5\x00\x00\x00\u07d4\xe4\u007f\xba\xed\x99\xfc \x99b`N\xbd \xe2@\xf7OE\x91\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe4\x82\xd2U\xed\xe5k\x04\xc3\xe8\xdf\x15\x1fV\xe9\xcab\xaa\xa8\u0089\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe4\x8ee\x12T!\x88\rB\xbd\xf1\x01\x8a\xb9w\x8d\x96\x92\x8f?\x89\u3bb5sr@\xa0\x00\x00\u07d4\u4481\x8a\xa6\x84\xe5\xa6vV\x1br]B\xf3\xccV\xaeQ\x98\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xe4\x996\xa9*\x8c\xcfq\x0e\xaa\xc3B\xbcEK\x9b\x14\xeb\ucc49lk\x93[\x8b\xbd@\x00\x00\u07d4\xe4\x9a\xf4\xf3J\u06a23\v\x0eI\xdct\xec\x18\xab/\x92\xf8'\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u46e0\u0356\x81lF\aw<\xf8\xa5\x97\v\xb5\xbc\x16\xa1\xe6\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xe4\xa4~93$l?\xd6)y\xa1\xea\x19\xff\u07ccr\xef7\x89\b\t\xb3\x83\xea}~\x80\x00\u07d4\u4dae\"\xc7s_[\x89\xf3M\xd7z\u0417_\n\u0311\x81\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe4\xca\nR8VM\xfc\x91\xe8\xbf\"\xba\xde)\x01a\x9a\x1c\u050965\u026d\xc5\u07a0\x00\x00\u07d4\xe4\xca\xfbr\u007f\xb5\u01b7\v\xb2u3\xb8\xa9\xcc\xc9\xefh\x88\xe1\x89\x10I{\xf4\xafL\xaf\x80\x00\u07d4\xe4\xdc\"\xedY[\xf0\xa37\xc0\x1e\x03\xcck\xe7D%_\xc9\xe8\x89\nZ\xa8P\t\xe3\x9c\x00\x00\u07d4\xe4\xfb&\xd1\xca\x1e\xec\xba=\x82\x98\xd9\xd1H\x11\x9a\u00bb\xf5\x80\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe4\xfc\x13\xcf\u02ec\x1b\x17\xcew\x83\xac\xd4#\xa8E\x94?k:\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe5\vFJ\xc9\xde5\xa5a\x8b|\xbf%Ft\x18+\x81\xb9~\x89\xdeB\xee\x15D\u0750\x00\x00\u07d4\xe5\x10,;q\x1b\x81\x03D\x19t\x19\xb1\u034apY\xf1>2\x89\x10CR\x8d\t\x84i\x80\x00\xe0\x94\xe5\x10\xd6y\u007f\xba=f\x93\x83Z\x84N\xa2\xadT\x06\x91\x97\x1b\x8a\x03\xae9\xd4s\x83\xe8t\x00\x00\u07d4\xe5\x14!\xf8\xee\"\x10\xc7\x1e\xd8p\xfea\x82v\u0215J\xfb\xe9\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\x1e\xb8~\u007f\xb71\x1fR(\xc4y\xb4\x8e\u0247\x881\xacL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5!V1\xb1BH\xd4Z%R\x96\xbe\xd1\xfb\xfa\x030\xff5\x89G\x03\xe6\xebR\x91\xb8\x00\x00\xe0\x94\xe5(\xa0\xe5\xa2g\xd6g\xe99:e\x84\xe1\x9b4\u071b\xe9s\x8a\x01/\x93\x9c\x99\xed\xab\x80\x00\x00\u07d4\xe54%\xd8\xdf\x1f\x11\xc3A\xffX\xae_\x148\xab\xf1\xcaS\u03c9\x11t\xa5\xcd\xf8\x8b\xc8\x00\x00\u07d4\xe5<hyb\x12\x03>No\x9c\xffV\xe1\x9cF\x1e\xb4T\xf9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5A\x02SM\xe8\xf2>\xff\xb0\x93\xb3\x12B\xad;#?\xac\xfd\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xe5E\xee\x84\xeaH\xe5d\x16\x1e\x94\x82\u055b\xcf@j`,\xa2\x89dI\xe8NG\xa8\xa8\x00\x00\u07d4\xe5H\x1a\u007f\xedB\xb9\x01\xbb\xed x\x9b\u052d\xe5\r_\x83\xb9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5Y\xb5\xfd3{\x9cUr\xa9\xbf\x9e\x0f%!\xf7\xd4F\xdb\xe4\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\\\x80R\n\x1b\x0fu[\x9a,\xd3\xce!Ov%e>\x8a\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe5mC\x13$\xc9)\x11\xa1t\x9d\xf2\x92p\x9c\x14\xb7ze\u034a\x01\xbc\x85\xdc*\x89\xbb \x00\x00\u07d4\xe5})\x95\xb0\xeb\xdf?<\xa6\xc0\x15\xeb\x04&\r\xbb\x98\xb7\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\u51f1j\xbc\x8at\b\x1e6\x13\xe1CB\xc03u\xbf\bG\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\x89\xfav\x98M\xb5\xec@\x04\xb4n\u8954\x92\xc3\aD\u0389\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xe5\x8d\xd228\xeen\xa7\xc2\x13\x8d8]\xf5\x00\xc3%\xf3v\xbe\x89b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xe5\x95?\xeaIq\x04\xef\x9a\xd2\xd4\xe5\x84\x1c'\x1f\a5\x19\u0089&)\xf6n\fS\x00\x00\x00\xe0\x94\u5587\x97F\x8e\xf7g\x10\x1bv\x1dC\x1f\xce\x14\xab\xff\u06f4\x8a\x01\xb3\xd9i\xfaA\x1c\xa0\x00\x00\u07d4\xe5\x97\xf0\x83\xa4i\xc4Y\x1c=+\x1d,w'\x87\xbe\xfe'\xb2\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe5\x9b;\xd3\x00\x89?\x97#>\xf9G\xc4or\x17\xe3\x92\xf7\xe9\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xa3e4<\xc4\xeb\x1ew\x03h\xe1\xf1\x14Jw\xb82\xd7\xe0\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe5\xa3\xd7\xeb\x13\xb1\\\x10\x01w#m\x1b\xeb0\xd1~\xe1T \x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xaa\v\x83;\xb9\x16\xdc\x19\xa8\xddh?\x0e\xde$\x1d\x98\x8e\xba\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\u5def\x14i\x86\xc0\xff\x8f\x85\xd2.l\xc34\a}\x84\xe8$\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xb8&\x19l\x0e\x1b\xc1\x11\x9b\x02\x1c\xf6\xd2Y\xa6\x10\u0256p\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe5\xb9o\u026c\x03\xd4H\xc1a:\xc9\x1d\x15\x97\x81E\xdb\xdf\u0449\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\u5e40\u048e\xec\xe2\xc0o\xcal\x94s\x06\x8b7\u0526\xd6\xe9\x89%\xaf\u058c\xac+\x90\x00\x00\u07d4\u5eb4\xf0\xaf\u0629\u0463\x81\xb4Wa\xaa\x18\xf3\xd3\xcc\xe1\x05\x89Q\xbf\xd7\xc18x\xd1\x00\x00\u07d4\xe5\xbc\u020c;%on\xd5\xfeU\x0eJ\x18\x19\x8b\x943V\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xbd\xf3OL\xccH>L\xa50\xcc|\xf2\xbb\x18\xfe\xbe\x92\xb3\x89\x06\xd85\xa1\v\xbc\xd2\x00\x00\u07d4\xe5\u0713I\xcbR\xe1a\x19a\"\u03c7\xa3\x896\xe2\xc5\u007f4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xe38\x00\xa1\xb2\xe9k\xde\x101c\n\x95\x9a\xa0\a\xf2nQ\x89Hz\x9a0E9D\x00\x00\u07d4\xe5\xe3~\x19@\x8f,\xfb\xec\x834\x9d\u0501S\xa4\xa7\x95\xa0\x8f\x89\u3bb5sr@\xa0\x00\x00\u07d4\xe5\xed\xc7>bo]4A\xa4U9\xb5\xf7\xa3\x98\u0153\xed\xf6\x89.\xe4IU\b\x98\xe4\x00\x00\u07d4\xe5\xed\xf8\x12?$\x03\xce\x1a\x02\x99\xbe\xcfz\xactM\a_#\x89\n\xdaUGK\x814\x00\x00\u07d4\xe5\xf8\xefm\x97\x066\xb0\u072aO \x0f\xfd\xc9\xe7Z\xf1t\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe5\xfb1\xa5\xca\xeej\x96\xde9;\xdb\xf8\x9f\xbee\xfe\x12[\xb3\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe5\xfb\xe3I\x84\xb67\x19o3\x1cg\x9d\f\fG\xd84\x10\xe1\x89llD\xfeG\xec\x05\x00\x00\u07d4\xe6\tU\xdc\v\xc1V\xf6\xc4\x18I\xf6\xbdwk\xa4K\x0e\xf0\xa1\x89\x10C\x16'\xa0\x93;\x00\x00\u07d4\xe6\nU\xf2\u07d9m\u00ee\xdbil\b\xdd\xe09\xb2d\x1d\xe8\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6\x11[\x13\xf9y_~\x95e\x02\xd5\aEg\u06b9E\xcek\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xe6\x1f(\t\x15\xc7t\xa3\x1d\"<\xf8\f\x06\x92f\xe5\xad\xf1\x9b\x89/\xb4t\t\x8fg\xc0\x00\x00\u07d4\xe6/\x98e\a\x12\xeb\x15\x87S\xd8)r\xb8\u9723\xf6\x18w\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe6/\x9d|d\xe8\xe2cZ\xeb\x88=\xd7;\xa6\x84\xee|\x10y\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe6>xt\x14\xb9\x04\x84x\xa5\a35\x9e\xcd\xd7\xe3dz\xa6\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe6FfXr\xe4\v\rz\xa2\xff\x82r\x9c\xaa\xba[\xc3\u8789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe6N\xf0\x12e\x8dT\xf8\xe8`\x9cN\x90#\xc0\x9f\xe8e\xc8;\x89\x01\x84\x93\xfb\xa6N\xf0\x00\x00\u07d4\xe6On\x1dd\x01\xb5l\akd\xa1\xb0\x86}\v/1\rN\x89\x02\u02edq\xc5:\xe5\x00\x00\u07d4\xe6g\xf6R\xf9W\u008c\x0ef\u04364\x17\xc8\f\x8c\x9d\xb8x\x89 \x9d\x92/RY\xc5\x00\x00\xe0\x94\xe6w\xc3\x1f\xd9\xcbr\x00u\u0724\x9f\x1a\xbc\xcdY\xec3\xf74\x8a\x01\xa6\u05be\xb1\xd4.\xe0\x00\x00\u07d4\xe6|,\x16e\u02038h\x81\x87b\x9fI\xe9\x9b`\xb2\u04fa\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xe6\x9al\xdb:\x8a}\xb8\xe1\xf3\f\x8b\x84\xcds\xba\xe0+\xc0\xf8\x8a\x03\x94\xfd\xc2\xe4R\xf6q\x80\x00\u07d4\xe6\x9d\x1c7\x8bw\x1e\x0f\xef\xf0Q\xdbi\xd9f\xacgy\xf4\xed\x89\x1d\xfaj\xaa\x14\x97\x04\x00\x00\u07d4\xe6\x9f\xcc&\xed\"_{.7\x984\xc5$\xd7\f\x175\u5f09lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\xa3\x01\x0f\x02\x01\xbc\x94\xffg\xa2\xf6\x99\xdf\xc2\x06\xf9\xe7gB\x89/\xa7\xcb\xf6dd\x98\x00\x00\u07d4\xe6\xa6\xf6\xddop\xa4V\xf4\xec\x15\xefz\xd5\xe5\u06f6\x8b\xd7\u0709\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe6\xb2\x0f\x98\n\xd8S\xad\x04\xcb\xfc\x88|\xe6`\x1ck\xe0\xb2L\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\u6cec?]M\xa5\xa8\x85}\v?0\xfcK+i+w\u05c9O%\x91\xf8\x96\xa6P\x00\x00\u07d4\xe6\xb9T_~\u0406\xe5R\x92F9\xf9\xa9\xed\xbb\xd5T\v>\x89\xcb\xd4{n\xaa\x8c\xc0\x00\x00\xe0\x94\xe6\xbc\xd3\n\x8f\xa18\xc5\xd9\xe5\xf6\xc7\xd2\u0680i\x92\x81-\u034a7\x0e\xa0\xd4|\xf6\x1a\x80\x00\x00\u07d4\xe6\xc8\x1f\xfc\xec\xb4~\xcd\xc5\\\vq\xe4\x85_>^\x97\xfc\x1e\x89\x12\x1e\xa6\x8c\x11NQ\x00\x00\u07d4\xe6\xcb&\vqmL\n\xb7&\xee\xeb\a\xc8pr\x04\xe2v\xae\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe6\xcb?1$\xc9\xc9\xcc84\xb1'K\xc33dV\xa3\x8b\xac\x89\x17+\x1d\xe0\xa2\x13\xff\x00\x00\xe0\x94\xe6\xd2\"\t\xff\u0438u\t\xad\xe3\xa8\xe2\xefB\x98y\u02c9\xb5\x8a\x03\xa7\xaa\x9e\x18\x99\xca0\x00\x00\u07d4\xe6\u051f\x86\xc2(\xf4sg\xa3^\x88l\xaa\xcb'\x1eS\x94)\x89\x16^\xc0\x9d\xa7\xa1\x98\x00\x00\u07d4\xe6\xe6!\xea\xab\x01\xf2\x0e\xf0\x83k|\xadGFL\xb5\xfd<\x96\x89\x11!\x93B\xaf\xa2K\x00\x00\u07d4\xe6\xe8\x861{jf\xa5\xb4\xf8\x1b\xf1d\xc58\xc2d5\x17e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe6\u98ddu\x0f\xe9\x949N\xb6\x82\x86\xe5\xeab\xa6\x99x\x82\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xe6\xec\\\xf0\u011b\x9c1~\x1epc\x15\uf7b7\xc0\xbf\x11\xa7\x8a\x03\xa4i\xf3F~\x8e\xc0\x00\x00\u07d4\xe6\xf5\xebd\x9a\xfb\x99Y\x9cAK'\xa9\xc9\xc8U5\u007f\xa8x\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xe6\xfe\n\xfb\x9d\xce\xdd7\xb2\xe2,E\x1b\xa6\xfe\xabg4\x803\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe7\x10\xdc\u041b\x81\x01\xf9C{\xd9}\xb9\ns\xef\x99=\v\xf4\x89\x14\xee6\xc0Z\xc2R\x00\x00\u07d4\xe7'\xe6~\xf9\x11\xb8\x1fl\xf9\xc7?\xcb\xfe\xbc+\x02\xb5\xbf\u0189lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7.\x1d3\\\u009a\x96\xb9\xb1\xc0/\x00:\x16\xd9q\xe9\v\x9d\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xe71\x1c\x953\xf0\t,rH\xc9s\x9b[,\x86J4\xb1\u0389\x97\xf9}l\xc2m\xfe\x00\x00\u07d4\xe7;\xfe\xad\xa6\xf0\xfd\x01o\xbc\x84>\xbc\xf6\xe3p\xa6[\xe7\f\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe7<\xcfCg%\xc1Q\xe2U\xcc\xf5!\f\xfc\xe5\xa4?\x13\xe3\x89\x01\x15NS!}\xdb\x00\x00\u07d4\xe7B\xb1\xe6\x06\x9a\x8f\xfc<Gg#]\xef\xb0\u051c\xbe\xd2\"\x89+^:\xf1k\x18\x80\x00\x00\u07d4\xe7F\b\xf5\x06\x86j\xdak\xfb\xfd\xf2\x0f\xeaD\v\xe7i\x89\xef\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xe7S>'\f\xc6\x1f\xa1d\xac\x15SE\\\x10]\x04\x88~\x14\x89\x06\x96\xd8Y\x00 \xbb\x00\x00\u07d4\xe7\\\x1f\xb1w\b\x9f>X\xb1\x06y5\xa6Yn\xf1s\u007f\xb5\x89\x05j\x87\x9f\xa7uG\x00\x00\u07d4\xe7\\;8\xa5\x8a?3\xd5V\x90\xa5\xa5\x97f\xbe\x18^\x02\x84\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7a\xd2\u007f\xa3P,\xc7k\xb1\xa6\bt\x0e\x14\x03\u03dd\xfci\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xe7f\xf3O\xf1o<\xfc\xc9s!r\x1fC\xdd\xf5\xa3\x8b\f\xf4\x89T\x06\x923\xbf\u007fx\x00\x00\u07d4\xe7m\x94Z\xa8\x9d\xf1\xe4W\xaa4+1\x02\x8a^\x910\xb2\u03897\b\xba\xed=h\x90\x00\x00\u07d4\xe7s^\xc7e\x18\xfcj\xa9-\xa8qZ\x9e\xe3\xf6%x\x8f\x13\x89lM\x16\v\xaf\xa1\xb7\x80\x00\xe0\x94\xe7z\x89\xbdE\xdc\x04\xee\xb4\xe4\x1d{Ykp~nQ\xe7L\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xe7}}\uac96\u0234\xfa\a\xca;\xe1\x84\x16=Zm`l\x89\x05\x049\x04\xb6q\x19\x00\x00\u07d4\xe7\u007f\xeb\xab\xdf\b\x0f\x0f]\xca\x1d?Wf\xf2\xa7\x9c\x0f\xfa|\x89K\"\x9d(\xa8Ch\x00\x00\xe0\x94\u7025c\x06\xba\x1ek\xb31\x95,\"S\x9b\x85\x8a\xf9\xf7}\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\xe7\x81\xecs-@\x12\x02\xbb\x9b\xd18`\x91\r\xd6\u009a\xc0\xb6\x89C8t\xf62\xcc`\x00\x00\u07d4\xe7\x84\xdc\xc8s\xaa\x8c\x15\x13\xec&\xff6\xbc\x92\xea\xc6\xd4\xc9h\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\x91-L\xf4V,W=\xdc[q\xe3s\x10\xe3x\xef\x86\u0249\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xe7\x91\u0545\xb8\x996\xb2])\x8f\x9d5\xf9\xf9\xed\xc2Z)2\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\x924\x9c\xe9\xf6\xf1O\x81\xd0g@\x96\xbe\xfa\x1f\x92!\xcd\xea\x89[]#J\r\xb48\x80\x00\u07d4\xe7\x96\xfdN\x83\x9bL\x95\xd7Q\x0f\xb7\xc5\xc7+\x83\xc6\xc3\xe3\u01c9\x1b\xc43\xf2?\x83\x14\x00\x00\xe0\x94\xe7\xa4/Y\xfe\xe0t\xe4\xfb\x13\xea\x9eW\xec\xf1\xccH(\"I\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xe7\xa4V\f\x84\xb2\x0e\x0f\xb5LIg\f)\x03\xb0\xa9lB\xa4\x89 j\xea\u01e9\x03\x98\x00\x00\u07d4\xe7\xa8\xe4q\xea\xfby\x8fET\xccnRg0\xfdV\xe6,}\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u7f82\xc6Y<\x1e\xed\xdd*\xe0\xb1P\x01\xff \x1a\xb5{/\x89\x01\t\x10\xd4\xcd\xc9\xf6\x00\x00\u07d4\xe7\u01b5\xfc\x05\xfct\x8e[C\x81rdI\xa1\xc0\xad\x0f\xb0\xf1\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe7\xd1u$\xd0\v\xad\x82I|\x0f'\x15jd\u007f\xf5\x1d'\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xe7\xd2\x13\x94\u007f\u02d0J\xd78H\v\x1e\xed/\\2\x9f'\xe8\x89\x01\x03\u00f1\xd3\xe9\xc3\x00\x00\u07d4\xe7\xd6$\x06 \xf4,^\u06f2\xed\xe6\xae\xc4=\xa4\xed\x9bWW\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xda`\x9d@\xcd\xe8\x0f\x00\xce[O\xfbj\xa9\u04304\x94\xfc\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe7\xf0oi\x9b\xe3\x1cD\vC\xb4\xdb\x05\x01\xec\x0e%&\x16D\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe7\xf4\xd7\xfeoV\x1f\u007f\xa1\xda0\x05\xfd6TQ\xad\x89\u07c9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe7\xfd\x8f\xd9Y\xae\xd2v~\xa7\xfa\x96\f\xe1\xdbS\xaf\x80%s\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe8\x0e\u007f\xef\x18\xa5\xdb\x15\xb0\x14s\xf3\xadkx\xb2\xa2\xf8\xac\u0649\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xe8\x13\u007f\xc1\xb2\xec|\xc7\x10:\xf9!\x89\x9bJ9\xe1\xd9Y\xa1\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xe8\x1c-4l\n\xdfL\xc5g\b\xf69K\xa6\xc8\u0226J\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8,X\xc5yC\x1bg5F\xb5:\x86E\x9a\xca\xf1\u079b\x93\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xe84\xc6C\x18 \\\xa7\xddJ!\xab\xcb\b&l\xb2\x1f\xf0,\x8965\xc6 G9\u0640\x00\u07d4\xe86\x04\xe4\xffk\xe7\xf9o`\x18\xd3\xec0r\xecR]\xffk\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\xe0\x94\xe8E\xe3\x87\xc4\xcb\u07d8\"\x80\xf6\xaa\x01\xc4\x0eK\xe9X\u0772\x8a\x05K@\xb1\xf8R\xbd\xa0\x00\x00\u07d4\xe8H\xca~\xbf\xf5\xc2O\x9b\x9c1g\x97\xa4;\xf7\xc3V)-\x89\x06.\x11\\\x00\x8a\x88\x00\x00\u07d4\xe8KU\xb5%\xf1\x03\x9etK\x91\x8c\xb33$\x92\xe4^\xcaz\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe8O\x80v\xa0\xf2\x96\x9e\xcd3>\xef\x8d\xe4\x10B\x98b\x91\xf2\x89\x17k4O*x\xc0\x00\x00\u07d4\xe8d\xfe\xc0~\xd1!Je1\x1e\x11\xe3)\xde\x04\r\x04\xf0\xfd\x89Y\u0283\xf5\xc4\x04\x96\x80\x00\u07d4\xe8}\xba\xc66\xa3w!\xdfT\xb0\x8a2\xefIY\xb5\xe4\xff\x82\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe8~\x9b\xbf\xbb\xb7\x1c\x1at\ft\xc7#Bm\xf5]\x06=\u064a\x01\xb1\x92\x8c\x00\u01e68\x00\x00\u07d4\xe8~\xacm`+A\t\xc9g\x1b\xf5{\x95\f,\xfd\xb9\x9dU\x89\x02\xb4\xf2\x19r\xec\xce\x00\x00\xe0\x94\u807b\xbeir-\x81\xef\xec\xaaH\u0455*\x10\xa2\xbf\xac\x8f\x8a\x03c\\\x9a\xdc]\xea\x00\x00\x00\u07d4\xe8\x92Is\x8b~\xce\xd7\xcbfjf<I\xcb\xf6\u0783C\xea\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\x9c\"\xf1\xa4\xe1\xd4tn\u03eaY\xed8o\xee\x12\xd5\x1e7\x89\x02o\x8e\x87\xf0\xa7\xda\x00\x00\u07d4\u8769n\x06\xbe\xafk\u0600\xb3x\xf0h\fC\xfd.\x9d0\x89 \x9a\x1a\x01\xa5o\xec\x00\x00\u07d4\xe8\xa9\x1d\xa6\xcf\x1b\x9de\xc7J\x02\xec\x1f\x96\xee\xcbm\xd2A\xf3\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\u8a64\x17@\xf4OT\xc3h\x8bS\xe1\xdd\xd4.C\xc9\xfe\x94\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\u8c8a\u0369qrWi\u06cfV=(fmA\u076bl\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xe8\xbe$\xf2\x89D>\xe4s\xbcv\x82/U\t\x8d\x89\xb9\x1c\u0149lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xc3\u04f0\xe1\u007f\x97\xd1\xe7V\xe6\x84\xf9N\x14p\xf9\x9c\x95\xa1\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xe8\xc3\xf0E\xbb}8\xc9\xd2\U000d5c3a\x84\x92\xb2S#\t\x01\x8a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\xe8\xccC\xbcO\x8a\xcf9\xbf\xf0N\xbf\xbfB\xaa\xc0j2\x84p\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xe8\xd9B\xd8/\x17^\xcb\x1c\x16\xa4\x05\xb1\x01C\xb3\xf4k\x96:\x89\x1e\xd2\xe8\xffm\x97\x1c\x00\x00\u07d4\xe8\u077e\xd72\xeb\xfeu@\x96\xfd\xe9\bk\x8e\xa4\xa4\xcd\xc6\x16\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe8\xder^\xca]\xef\x80_\xf7\x94\x1d1\xac\x1c.4-\xfe\x95\x89\x85~\ro\x1d\xa7j\x00\x00\u07d4\xe8\xe9\x85\x05\x86\xe9OR\x99\xabIK\xb8!\xa5\xf4\f\x00\xbd\x04\x89\xcf\x15&@\xc5\xc80\x00\x00\xe0\x94\xe8\xea\u047b\x90\xcc\u00ee\xa2\xb0\xdc\u0175\x80VUFU\xd1\u054a\x01\xa4\xab\xa2%\xc2\a@\x00\x00\u07d4\xe8\xea\xf1)D\t-\xc3Y\x9b9S\xfa|\xb1\xc9v\x1c\xc2F\x89a\x94\x04\x9f0\xf7 \x00\x00\xe0\x94\xe8\xedQ\xbb\xb3\xac\xe6\x9e\x06\x02K3\xf8hD\xc4sH\u06de\x8a\"\xf9\xea\x89\xf4\xa7\xd6\xc4\x00\x00\u07d4\xe8\xef\x10\r|\xe0\x89X2\xf2g\x8d\xf7-J\u03cc(\xb8\xe3\x89\x1b\x1bk\u05efd\xc7\x00\x00\u07d4\xe8\xf2\x99i\xe7\\e\xe0\x1c\xe3\xd8aT }\n\x9e|v\xf2\x89\xa2/\xa9\xa7:'\x19\x80\x00\u07d4\xe8\xfc6\xb0\x13\x1e\xc1 \xac\x9e\x85\xaf\xc1\f\xe7\vV\u0636\xba\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9\n5L\xec\x04\u059e]\x96\xdd\xc0\xc5\x13\x8d=3\x15\n\xa0\x89\x1b\x1a}\u03caD\u04c0\x00\xe0\x94\xe9\x13>}1\x84]_+f\xa2a\x87\x92\xe8i1\x1a\xcff\x8a\x05\x17\xc0\xcb\xf9\xa3\x90\x88\x00\x00\u07d4\xe9\x1d\xac\x01\x95\xb1\x9e7\xb5\x9bS\xf7\xc0\x17\xc0\xb29[\xa4L\x89e\xea=\xb7UF`\x00\x00\u07d4\xe9\x1f\xa0\xba\xda\u0779\xa9~\x88\xd3\xf4\xdb|U\u05bbt0\xfe\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xe9#\xc0aw\xb3B~\xa4H\xc0\xa6\xff\x01\x9bT\xccT\x8d\x95\x89\x01\xf7\x80\x01Fg\xf2\x80\x00\xe0\x94\xe9=G\xa8\u0288]T\fNRo%\xd5\xc6\xf2\xc1\b\u0138\x8a\x17\xda:\x04\u01f3\xe0\x00\x00\x00\u07d4\xe9E\x8fh\xbb',\xb5g:\x04\xf7\x81\xb4\x03Uo\u04e3\x87\x89\x03N\x8b\x88\xce\xe2\xd4\x00\x00\u07d4\xe9IA\xb6\x03`\x19\xb4\x01j0\xc1\x03}Zi\x03\xba\xba\xad\x89*H\xac\xabb\x04\xb0\x00\x00\u07d4\xe9I[\xa5\x84'(\xc0\ud5fe7\xd0\xe4\"\xb9\x8di ,\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9M\xed\x99\u0735r\xb9\xbb\x1d\u02e3/m\xee\x91\xe0W\x98N\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\xe0\x94\xe9QyR}\uc951l\xa9\xa3\x8f!\\\x1e\x9c\xe77\xb4\u024a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xe9U\x91\x85\xf1f\xfc\x95\x13\xccq\x11aD\xce-\xeb\x0f\x1dK\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xe9^\x92\xbb\xc6\xde\a\xbf:f\x0e\xbf_\xeb\x1c\x8a5'\xe1\u0148\xfc\x93c\x92\x80\x1c\x00\x00\xe0\x94\xe9e\u06a3@9\xf7\xf0\xdfb7Z7\u5acar\xb3\x01\xe7\x8a\x01\x03\xfd\xde\u0373\xf5p\x00\x00\u07d4\xe9i\xea\x15\x95\xed\xc5\u0127\a\xcf\xde8\t)c2Q\xa2\xb0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9k\x18N\x1f\x0fT\x92J\xc8t\xf6\v\xbfDptF\xb7+\x89\x9d\xcc\x05\x15\xb5n\f\x00\x00\xe0\x94\xe9m}L\xdd\x15U:NM1mmd\x80\xca<\xea\x1e8\x8a\x02\x95]\x02\xe1\xa15\xa0\x00\x00\u07d4\xe9n-8\x13\xef\xd1\x16_\x12\xf6\x02\xf9\u007fJb\x90\x9d<f\x89|\xae\xe9v\x13\xe6p\x00\x00\u07d4\xe9\u007f\xde\vgqc%\xcf\x0e\xcc\u8851\xa3v\x1b,y\x1d\x896w\x03n\xdf\n\xf6\x00\x00\u07d4\xe9\x82\xe6\xf2\x8cT\x8f_\x96\xf4^c\xf7\xabp\x87$\xf5?\xa1\x89\x15z\xe8)\xa4\x1f;\x00\x00\u07d4\xe9\x86L\x1a\xfc\x8e\xaa\xd3\u007f;\xa5o\xcbtw\xccb \t\xb7\x89\x04HXap\xa7\xdc\x00\x00\u07d4\xe9\x87\xe6\x13\x9eaF\xa7\x17\xfe\xf9k\xc2I4\xa5D\u007f\xe0]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9\x89s<\xa1\u054d\x9e{P)\xba]DHX\xbe\xc01r\x89\x1f\x87@\x83\x13\xdfO\x80\x00\u07d4\u9311\xca\u0752L\x92W\x9e\x11\xb4\x12\x17\xb2\x82\x95l\u06a1\x89\a\\\x9a\x84\x802\f\x00\x00\u07d4\xe9\x9a\xec\xe9\x05A\xca\xe2$\xb8}\xa6s\x96^\n\xeb)j\xfd\x891\u07d0\x95\xa1\x8f`\x00\x00\u07d4\xe9\x9d\xe2X\xa4\x17<\xe9\xac8\xed\xe2l\v;\xea<\ts\u0549Y\u0438\x05\xe5\xbb0\x00\x00\u07d4\u98b4\x91N\x85S\xbf\r|\x00\xcaS#i\xb8y\xf91\xbf\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\u98da\x8b\xac\x0f\x01\xc3I\xc6L\xed\xb6\x98\x97\xf63#N\u0489\xd7\xc1\x98q\x0ef\xb0\x00\x00\u07d4\u996e<\x9e\x05\x97}\xd1\x06\x9e\x9f\xd9\u04ee\xfb\xae\x04\xb8\u07c9j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xe9\xac67n\xfa\x06\x10\x9d@rc\a\xdd\x1aW\xe2\x13\uaa49\n\x84Jt$\xd9\xc8\x00\x00\u07d4\xe9\xb1\xf1\xfc\xa3\xfaG&\x9f!\xb0a\xc3S\xb7\xf5\xe9m\x90Z\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xe9\xb3o\xe9\xb5\x14\x12\xdd\xca\x1aR\x1dn\x94\xbc\x90\x12\x13\u0768\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\u9d24\x855w\xa9\xdb\xcc.y[\xe01\r\x1b\xed(d\x1a\x8965\u026d\xc5\u07a0\x00\x00\u07d4\u9da7\x90\x00\x9b\xc1fB\xc8\xd8 \xb7\xcd\xe0\xe9\xfd\x16\xd8\xf5\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\u9e62tu\x10\xe3\x10$\x1d.\u0398\xf5k3\x01\xd7W\xe0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xe9\xc3\\\x91<\xa1\xfc\xea\xb4aX/\u1941Qd\xb4\xfd!\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xe9\xc6\u07ee\x97\xf7\t\x9f\xc5\xf4\xe9KxM\xb8\x02\x92:\x14\x19\x89\x02\xa5<mrO\x10\x00\x00\u07d4\xe9\xc7X\xf8\xdaA\xe34nCP\xe5\xac9v4\\l\x10\x82\x89h\xa0\xd3\t(&\xad\x00\x00\u07d4\xe9\xca\xf8'\xbe\x9d`y\x15\xb3e\xc8?\r;~\xa8\u01dbP\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xe9\xca\xfeA\xa5\xe8\xbb\xd9\v\xa0-\x9e\x06X[N\xb5F\xc5\u007f\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xe9\u0559Ek%C\xe6\u06c0\xea\x9b!\x0e\x90\x80&\xe2\x14n\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xe9\xe1\xf7\xcb\x00\xa1\x10\xed\xd0\xeb\xf8\xb3w\xef\x8a{\xb8V\x11\u007f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xea\x14\xbf\xda\nnvf\x8f\x87\x882\x1f\a\xdf7\x82N\xc5\u07ca*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xea\x1e\xa0\u0159\xaf\xb9\xcd6\u02ac\xbb\xb5+[\xbb\x97Ysw\x899\xfb\xae\x8d\x04-\xd0\x00\x00\u07d4\xea\x1e\xfb<\u727e\xde\xc3\xd6|>\x1b;\xc0\xe9\xaa\"\u007f\x90\x89'\xcaK\xd7\x19\xf0\xb8\x00\x00\u07d4\xea,\x19}&\xe9\x8b\r\xa8>\x1br\u01c7a\x8c\x97\x9d=\xb0\x89\x01\x11du\x9f\xfb2\x00\x00\xe0\x94\xea7y\xd1J\x13\xf6\u01c5f\xbc\xde@5\x91A:b9\u06ca)\xb7d2\xb9DQ \x00\x00\u07d4\xeaN\x80\x9e&j\xe5\xf1<\xdb\u33dd\x04V\xe68m\x12t\x89\xf3\xf2\v\x8d\xfai\xd0\x00\x00\xe0\x94\xeaS\xc9T\xf4\xed\x97\xfdH\x10\x11\x1b\u06b6\x9e\xf9\x81\xef%\xb9\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xeaS\xd2ed\x85\x9d\x9e\x90\xbb\x0eS\xb7\xab\xf5`\xe0\x16,8\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xea`Ci\x12\xdek\xf1\x87\u04e4r\xff\x8fS3\xa0\xf7\xed\x06\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xea`T\x9e\xc7U?Q\x1d!I\xf2\xd4fl\xbd\x92C\xd9<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeaf\xe7\xb8M\u037f6\xee\xa3\xe7[\x858*u\xf1\xa1]\x96\x89]\xbc\x91\x91&o\x11\x80\x00\u07d4\xeahlPW\t<\x17\x1cf\u06d9\xe0\x1b\x0e\xce\xcb0\x86\x83\x89\x14\u0768],\xe1G\x80\x00\u07d4\xeaj\xfe,\xc9(\xac\x83\x91\xeb\x1e\x16_\xc4\x00@\xe3t!\u7262\u007f\xa0c\xb2\xe2\xe6\x80\x00\u07d4\xeay\x05}\xab\xef^d\xe7\xb4O\u007f\x18d\x8e~S7\x18\u0489\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xea|Mm\xc7)\xcdk\x15|\x03\xad#|\xa1\x9a \x93F\u00c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\x81h\xfb\xf2%\xe7\x86E\x9c\xa6\xbb\x18\xd9c\xd2kPS\t\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xea\x81\u02868T\f\xd9\xd4\xd7=\x06\x0f,\xeb\xf2$\x1f\xfc>\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xea\x83\x17\x19yYB@A\xd9\xd7\xc6z>\xce\x1d\xbbx\xbbU\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xea\x85'\xfe\xbf\xa1\xad\xe2\x9e&A\x93)\u04d3\xb9@\xbb\xb7\u0709lj\xccg\u05f1\xd4\x00\x00\u07d4\xea\x8f0\xb6\xe4\xc5\xe6R\x90\xfb\x98d%\x9b\u0159\x0f\xa8\ue289\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\x94\xf3(\b\xa2\uf29b\xf0\x86\x1d\x1d$\x04\xf7\xb7\xbe%\x8a\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xea\xa4\\\xea\x02\xd8},\xc8\xfd\xa9CN-\x98[\xd4\x03\x15\x84\x89h\x1f\xc2\xccn+\x8b\x00\x00\xe0\x94\uac3d\x14\x83\t\x18l\xf8\xcb\xd1;r2\xd8\tZ\u02c3:\x8a\x02C\x9a\x88\x1cjq|\x00\x00\u07d4\uaed0\xd3y\x89\xaa\xb3\x1f\xea\xe5G\xe0\xe6\xf3\x99\x9c\xe6\xa3]\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xc0\x82~\xff\fn?\xf2\x8a}JT\xf6\\\xb7h\x9d{\x99\x89\x9a\xd9\u67ddGR\x00\x00\u07d4\xea\xc1H(&\xac\xb6\x11\x1e\x19\xd3@\xa4_\xb8QWk\xed`\x89\x01\xbe\x8b\xab\x04\u067e\x80\x00\xe0\x94\xea\xc1{\x81\xedQ\x91\xfb\b\x02\xaaT3s\x13\x83A\a\xaa\xa4\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xea\u00efW\x84\x92\u007f\u9958\xfcN\xec8\xb8\x10/7\xbcX\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xea\u01b9\x88BT.\xa1\v\xb7O&\xd7\xc7H\x8fi\x8bdR\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xea\xc7h\xbf\x14\xb8\xf9C.i\xea\xa8*\x99\xfb\xeb\x94\xcd\f\x9c\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xea\xd2\x1c\x1d\xec\u03ff\x1c\\\xd9f\x88\xa2Gki\xba\a\xceJ\x89\x03\xf2M\x8eJ\x00p\x00\x00\u07d4\xea\xd4\xd2\xee\xfbv\xab\xaeU3\x96\x1e\xdd\x11@\x04\x06\xb2\x98\xfc\x89\xd2U\xd1\x12\xe1\x03\xa0\x00\x00\u07d4\xea\xd6Rb\xed]\x12-\xf2\xb2u\x14\x10\xf9\x8c2\xd1#\x8fQ\x89\x05\x83\x17\xedF\xb9\xb8\x00\x00\u07d4\xea\xd7P\x16\u3801Pr\xb6\xb1\b\xbc\xc1\xb7\x99\xac\xf08>\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xea#\xaa\x05r\x00\xe7\xc9\xc1^\x8f\xf1\x90\xd0\xe6l\f\x0e\x83\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xea\xed\x16\xea\xf5\u06ab[\xf0)^^\a\u007fY\xfb\x82U\x90\v\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xea\xed\xcck\x8bib\xd5\xd9(\x8c\x15lW\x9dG\xc0\xa9\xfc\xff\x89\x04\x9b\x9c\xa9\xa6\x944\x00\x00\u07d4\xea\xf5#\x88Tn\xc3Z\xcaolc\x93\xd8\xd6\t\xde:K\xf3\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xeb\x10E\x8d\xac\xa7\x9eJk$\xb2\x9a\x8a\x8a\xdaq\x1b\u007f.\xb6\x89\u063beI\xb0+\xb8\x00\x00\u07d4\xeb\x1c\xea{E\u047dM\x0e*\x00{\u04ff\xb3Tu\x9e,\x16\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xeb%H\x1f\u035c\"\x1f\x1a\xc7\xe5\xfd\x1e\u0353\a\xa1b\x15\xb8\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\xe0\x94\xeb.\xf3\u04cf\xe6R@<\xd4\xc9\xd8^\xd7\xf0h,\xd7\xc2\u078a\t\x0fSF\b\xa7(\x80\x00\x00\xe0\x94\xeb;\xddY\xdc\u0765\xa9\xbb*\xc1d\x1f\xd0!\x80\xf5\xf3e`\x8a\x01e\xc9fG\xb3\x8a \x00\x00\u07d4\xeb<\xe7\xfc8\x1cQ\xdb}_\xbdi/\x8f\x9e\x05\x8aLp=\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xebE?Z:\xdd\u074a\xb5gP\xfa\xdb\x0f\xe7\xf9M\x9c\x89\xe7\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xebO\x00\xe2\x836\xea\t\x94%\x88\ueb12\x18\x11\xc5\"\x14<\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebR\xab\x10U4\x922\x9c\x1cT\x83:\xe6\x10\xf3\x98\xa6[\x9d\x89\b=lz\xabc`\x00\x00\u07d4\xebW\r\xba\x97R'\xb1\xc4-n\x8d\xea,V\u026d\x96\x06p\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xebc\x94\xa7\xbf\xa4\u0489\x11\u0565\xb2>\x93\xf3^4\f\"\x94\x89\x04:w\xaa\xbd\x00x\x00\x00\u07d4\xebh\x10i\x1d\x1a\xe0\u045eG\xbd\"\u03be\u0cfa'\xf8\x8a\x89\x87\x85c\x15\xd8x\x15\x00\x00\u07d4\xebvBL\x0f\u0557\xd3\xe3A\xa9d*\xd1\xee\x11\x8b+W\x9d\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb| +F+|\u0145]t\x84u_n&\xefC\xa1\x15\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\x83\\\x1a\x91\x18\x17\x87\x8a3\xd1gV\x9e\xa3\xcd\u04c7\xf3(\x8965\u026d\xc5\u07a0\x00\x00\u07d4\ub268\x82g\t\t\xcf7~\x9ex(n\xe9{\xa7\x8dF\u0089+|\xc2\xe9\xc3\"\\\x00\x00\xe0\x94\xeb\x90\u01d3\xb3S\x97a\xe1\xc8\x14\xa2\x96q\x14\x86\x92\x19>\xb4\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xeb\x9c\xc9\xfe\bi\xd2\u06b5,\u01ea\xe8\xfdW\xad\xb3_\x9f\xeb\x89j\x93\xbb\x17\xaf\x81\xf8\x00\x00\xe0\x94\ub8c8\xb0\xda'\xc8{\x1c\xc0\xea\xc6\xc5{,Z\vE\x9c\x1a\x8a\x01p\xa0\xf5\x04\x0eP@\x00\x00\u07d4\xeb\xaa!m\xe9\xccZC\x03\x17\a\xd3o\xe6\u057e\xdc\x05\xbd\xf0\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xeb\xac+D\b\xefT1\xa1;\x85\b\xe8bP\x98!\x14\xe1E\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xeb\xb6,\xf8\xe2,\x88K\x1b(\xc6\xfa\x88\xfb\xbc\x17\x93\x8a\xa7\x87\x89+By\x84\x03\u0278\x00\x00\u07d4\xeb\xb7\xd2\xe1\x1b\u01b5\x8f\n\x8dE\xc2\xf6\xde0\x10W\n\u0211\x89\x01s\x17\x90SM\xf2\x00\x00\u07d4\xeb\xbbO,=\xa8\xbe>\xb6-\x1f\xfb\x1f\x95\x02a\u03d8\xec\u0689lk\x93[\x8b\xbd@\x00\x00\u07d4\xeb\xbdM\xb9\x01\x99R\u058b\x1b\x0fm\x8c\xf0h<\x008{\xb5\x89\x12\x04\x01V=}\x91\x00\x00\u07d4\xeb\xbe\xeb%\x91\x84\xa6\xe0\x1c\xcc\xfc\"\a\xbb\u0603xZ\xc9\n\x89!\x9b\xc1\xb0G\x83\xd3\x00\x00\u07d4\xeb\xd3V\x15j81#4=H\x84;\xff\xeda\x03\xe8f\xb3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xeb\xd3{%ec\xe3\fo\x92\x89\xa8\xe2p/\bR\x88\b3\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xeb\xe4l\xc3\xc3L2\xf5\xad\xd6\xc3\x19[\xb4\x86\xc4q>\xb9\x18\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xeb\xff\x84\xbb\xefB0q\xe6\x04\xc3a\xbb\xa6w\xf5Y=\xefN\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xec\t'\xba\xc7\xdc6f\x9c(5J\xb1\xbe\x83\xd7\xee\xc3\t4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\x0e\x18\xa0\x1d\xc4\xdc]\xaa\xe5g\xc3\xfaL\u007f\x8f\x9bY\x02\x05\x89\x11\x1f\xfe@JA\xe6\x00\x00\xe0\x94\xec\x116,\xec\x81\t\x85\xd0\xeb\xbd{sE\x14D\x98[6\x9f\x8a\x06ZNIWpW1\x80\x00\u07d4\xec,\xb8\xb97\x8d\xff1\xae\xc3\xc2.\x0em\xad\xff1J\xb5\u0749lk\x93[\x8b\xbd@\x00\x00\u07d4\xec0\xad\u0749[\x82\xee1\x9eT\xfb\x04\xcb+\xb09q\xf3k\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec;\x8bX\xa1'\x03\xe5\x81\xce_\xfd~!\xc5}\x1e\\f?\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xecHg\xd2\x17Z\xb5\xb9F\x93aYUFUF\x84\u0364`\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xecM\b\xaa.GIm\u0287\"]\xe3?+@\xa8\xa5\xb3o\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\xecX\xbc\r\f \xd8\xf4\x94efAS\xc5\xc1\x96\xfeY\u6f89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xec[\x19\x8a\x00\u03f5Z\x97\xb5\xd56D\xcf\xfa\x8a\x04\u04abE\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec]\xf2'\xbf\xa8]z\xd7kBn\x1c\xee\x96;\xc7\xf5\x19\u074965\u026d\xc5\u07a0\x00\x00\xe0\x94\xec_\xea\xfe!\f\x12\xbf\u0265\xd0Y%\xa1#\xf1\xe7?\xbe\xf8\x8a`\x8f\xcf=\x88t\x8d\x00\x00\x00\u07d4\xeci\x04\xba\xe1\xf6\x97\x90Y\x17\t\xb0`\x97\x83s?%s\xe3\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\xe0\x94\xecs\x11L^@o\u06fe\t\xb4\xfab\x1b\xd7\x0e\xd5N\xa1\xef\x8a\x050%\xcd!o\xceP\x00\x00\u07d4\xecs\x83=\xe4\xb8\x10\xbb\x02x\x10\xfc\x8fi\xf5D\xe8<\x12\u044965\u026d\xc5\u07a0\x00\x00\u07d4\xecu\xb4\xa4u\x13\x12\v\xa5\xf8`9\x81O\x19\x98\xe3\x81z\u00c9\t\xb0\xbc\xe2\xe8\xfd\xba\x00\x00\u07d4\xecv\xf1.W\xa6U\x04\x03?,\v\xceo\xc0;\xd7\xfa\n\u0109\xc2\x12z\xf8X\xdap\x00\x00\u0794\xec\x80\x14\xef\xc7\xcb\xe5\xb0\xceP\xf3V,\xf4\xe6\u007f\x85\x93\xcd2\x88\xf0\x15\xf2W6B\x00\x00\u07d4\xec\x82\xf5\r\x06G_hM\xf1\xb3\x92\xe0\r\xa3A\xaa\x14TD\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xec\x83\xe7\x98\u00d6\xb7\xa5^*\"$\xab\u0343K'\xeaE\x9c\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\x89\xf2\xb6x\xa1\xa1[\x914\xec^\xb7\fjb\a\x1f\xba\xf9\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\x8c\x1d{j\xac\xcdB\x9d\xb3\xa9\x1e\xe4\xc9\xeb\x1c\xa4\xf6\xf7<\x89\xe6d\x99\"\x88\xf2(\x00\x00\xe0\x94\xec\x98Q\xbd\x91rpa\x02g\xd6\x05\x18\xb5M<\xa2\xb3[\x17\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xec\x99\xe9]\xec\xe4o\xff\xfb\x17^\xb6@\x0f\xbe\xbb\b\ue6d5\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xec\xa5\xf5\x87\x92\xb8\xc6-*\xf5Vq~\xe3\xee0(\xbeM\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xabZ\xba[\x82\x8d\xe1pS\x81\xf3\x8b\xc7D\xb3+\xa1\xb47\x892\xf5\x1e\u06ea\xa30\x00\x00\u07d4\xec\xaf3P\xb7\xce\x14M\x06\x8b\x18`\x10\x85,\x84\xdd\f\xe0\xf0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xec\xb9LV\x8b\xfeY\xad\xe6Pd_O&0lsl\xac\xe4\x89\x0e~\xeb\xa3A\vt\x00\x00\xe0\x94\xec\xbeB^g\r9\tN \xfbVC\xa9\xd8\x18\xee\xd26\u078a\x01\x0f\f\xf0d\xddY \x00\x00\xe0\x94\xec\xbe^\x1c\x9a\u04b1\xdc\xcf\n0_\xc9R/Fi\xdd:\xe7\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xec\xcfz\x04W\xb5f\xb3F\xcag:\x18\x0fDA0!j\u00c9\x05k\xc7^-c\x10\x00\x00\u07d4\xec\u0466(\x025\x1aAV\x8d#\x030\x04\xac\xc6\xc0\x05\xa5\u04c9\x02\xb5\xe3\xaf\x16\xb1\x88\x00\x00\u07d4\xec\xd2v\xafd\u01dd\x1b\u0669+\x86\xb5\u835a\x95\xeb\x88\xf8\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xec\u0506\xfc\x19g\x91\xb9,\xf6\x12\xd3HaO\x91VH\x8b~\x8a\x02\x8a\x85t%Fo\x80\x00\x00\u07d4\xec\xda\xf92)\xb4^\xe6r\xf6]\xb5\x06\xfb^\xca\x00\xf7\xfc\xe6\x89W\x01\xf9m\xcc@\xee\x80\x00\u07d4\xec\xe1\x11g\vV<\u037e\xbc\xa5#\x84)\x0e\xcdh\xfe\\\x92\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xec\xe1\x15&\x82\xb7Y\x8f\xe2\xd1\xe2\x1e\xc1U3\x88T5\xac\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xec\xe1)\bw\xb5\x83\xe3a\xa2\xd4\x1b\x00\x93F\xe6'N%8\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xec\xf0]\a\xea\x02n~\xbfIA\x00#5\xba\xf2\xfe\xd0\xf0\x02\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xec\xf2L\xdd|\"\x92\x8cD\x1eiM\xe4\xaa1\xb0\xfa\xb5\x97x\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xec\xfd\x00M\x02\xf3l\xd4\u0634\xa8\xc1\xa9S;j\xf8\\\xd7\x16\x8a\x01\x0fA\xac\xb4\xbb;\x9c\x00\x00\xe0\x94\xed\x02\x06\xcb#1Q(\xf8\xca\xff&\xf6\xa3\v\x98Tg\xd0\"\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xed\x10e\xdb\u03dds\xc0O\xfcy\b\x87\r\x88\x14h\xc1\xe12\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\x12vQ;o\u0186(\xa7A\x85\xc2\xe2\f\xbb\xcax\x17\xbf\x89\nZ\xa8P\t\xe3\x9c\x00\x00\xe0\x94\xed\x12\xa1\xba\x1f\xb8\xad\xfc\xb2\r\xfa\x19X.RZ\xa3\xb7E$\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xed\x16\xce9\xfe\xef;\xd7\xf5\xd1b\x04^\x0fg\xc0\xf0\x00F\xbb\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xed\x1a\\C\xc5t\xd4\xe94)\x9b$\xf1G,\u071f\xd6\xf0\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xed\x1b$\xb6\x91-Q\xb34\xac\r\xe6\xe7q\xc7\xc0EF\x95\xea\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xed\x1f\x1e\x11Z\r`\xce\x02\xfb%\xdf\x01M(\x9e:\f\xbe}\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xed10\\1\x9f\x92s\u04d3m\x8f[/q\u9c72)c\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xed2z\x14\xd5\u03ed\u0641\x03\xfc\t\x99q\x8d~\xd7\x05(\xea\x89N\x10\x03\xb2\x8d\x92\x80\x00\x00\u07d4\xed<\xbc7\x82\u03bdg\x98\x9b0\\A3\xb2\xcd\xe3\"\x11\xeb\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xed@\x14S\x8c\xeefJ/\xbc\xb6\xdcf\x9fz\xb1m\v\xa5|\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedA\u188f\\\xaa\x848\x80\xefN\x8b\b\xbdl3\x14\x1e\u07c9*\xd5\xdd\xfaz\x8d\x83\x00\x00\xe0\x94\xedK\xe0J\x05-z\u0333\xdc\u03901\x9d\xba@ \xab,h\x8a\a\xf3zp\xea\xf3b\x17\x80\x00\xe0\x94\xedR\xa2\xcc\bi\u071e\x9f\x84+\u0415|G\xa8\xe9\xb0\xc9\xff\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xed[LA\xe7b\xd9B@Cs\xca\xf2\x1e\xd4a]%\xe6\xc1\x89m-O=\x95%\xb4\x00\x00\u07d4\xed`\u012bnT\x02\x061~5\x94zc\xa9\xcak\x03\xe2\u02c9\x03\x1a\u066d\vF\u007f\x80\x00\u07d4\xedd\x1e\x066\x8f\xb0\xef\xaa\x17\x03\xe0\x1f\xe4\x8fJhS\t\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xedfC\xc0\xe8\x88K-2\x11\x857\x85\xa0\x8b\xf8\xf3>\u049f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xedp\xa3|\xdd\x1c\xbd\xa9tm\x93\x96X\xae*a\x81(\x85x\x8a\x02\bj\xc3Q\x05&\x00\x00\x00\u07d4\xedsFvn\x1agm\r\x06\xec\x82\x18g\xa2v\xa0\x83\xbf1\x89\u064a\t1\xcc-I\x00\x00\u07d4\xed\x86&\x16\xfc\xbf\xb3\xbe\xcbt\x06\xf7<\\\xbf\xf0\f\x94\aU\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xed\x9e\x03\f\xa7\\\xb1\u049e\xa0\x1d\rL\xdf\xdc\xcd8D\xb6\xe4\x89\x01\xac\xc1\x16\u03ef\xb1\x80\x00\xe0\x94\ud7bc\u02e4/\x98\x15\xe7\x823&m\xd6\xe85\xb6\xaf\xc3\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\ud7f1\xf5\xaf/\xbf\u007f\xfcP)\xce\xe4+p\xff\\'[\xf5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xed\xa4\xb2\xfaY\u0584\xb2z\x81\r\xf8\x97\x8as\xdf0\x8ac\u0089\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xed\xb4s59y\xa2\x06\x87\x9d\xe1D\xc1\n<Q\xd7\xd7\b\x1a\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xed\xb7\x1e\xc4\x1b\xda}\u0386\xe7f\xe6\xe8\xc3\xe9\x90w#\xa6\x9b\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xed\xba\xc9R{T\xd6\xdfz\xe2\xe0\x00\u0323a;\xa0\x15\xca\xe3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xed\xc2/\xb9,c\x8e\x1e!\xff\\\xf09\u06a6\xe74\xda\xfb)\x89\x10'\x94\xad \xdah\x00\x00\xe0\x94\xed\xda\u0354\uc262\uf58f\u03d7z\b\xf1\xfa\xe2uxi\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xed\u06ea\xfb\xc2\x1b\xe8\xf2Ub\xf1\xedm\x05\u05af\xb5\x8f\x02\u0089lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\xe0\x14~\xc02\xc3a\x83\x10\xc1\xff%i\v\xf1r\x19=\xac\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xed\xe5\xde|\u007f\xb7\xee\xe0\xf3ndS\nAD\x0e\u07fe\xfa\u03c9!u^\xe1\xef+\x18\x00\x00\u07d4\xed\xe7\x9a\xe1\xffO\x16\x06\u0552p!o\xa4j\xb2\xdd\xd4\uca89\a\xea(2uw\b\x00\x00\xe0\x94\xed\xe8\xc2\u02c7o\xbe\x8aL\u0282\x906\x1a~\xa0\x1ai\xfd\xf8\x8a\x01\xa7\x8ckD\xf8A\x83\x80\x00\u07d4\xed\xebH\x94\xaa\xdd\x00\x81\xbb\xdd\xd3\xe8\x84h\x04\xb5\x83\u045f'\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xed\xf6\x03\x89\x02(\xd7\xd5\u0793\t\x94+\\\xadB\x19\xef\x9a\u05ca\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xed\xf8\xa3\xe1\xd4\x0f\x13\xb7\x9e\xc8\xe3\xe1\xec\xf2b\xfd\x92\x11bc\x89\b\x90\xb0\xc2\xe1O\xb8\x00\x00\u07d4\xed\xfd\xa2\xd5\u06d8\xf98\a\x14fMT\xb4\xee\x97\x1a\x1c\xae\x03\x89\x02+\xb8\xdd\xd6y\xbe\x00\x00\u07d4\xee\x00\a\xb0\x96\r\x00\x90\x8a\x94C*suW\x87j\xac|1\x89\x02\xe0B\x1ei\xc4\u0300\x00\u07d4\xee\x04\x9a\xf0\x05\x97M\xd1\u01f3\xa9\u028d\x9a\xa7qu\xbaS\xaa\x89\x12\x11\xec\xb5m\x13H\x80\x00\u07d4\xee%\xb9\xa7\x03&y\xb1\x13X\x8e\xd5,\x13}\x1a\x05:\x1e\x94\x89\n\xd5\x0f?N\xea\x8e\x00\x00\u07d4\xee1\x16\u007f\x9c\xc9;<de`\x9dy\xdb\f\u0790\xe8HL\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xee4\xc7\xe7\x99]\xb9\xf1\x87\xcf\xf1V\x91\x8c\xfbo\x13\xf6\xe0\x03\x89j@v\xcfy\x95\xa0\x00\x00\u07d4\xee5d\xf5\xf1\xba\x0f\x94\xec{\xac\x16K\u077f1\u0188\x8bU\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xeeX\xfb=\xb2\x90p\xd0\x13\x01\x88\xceG+\xe0\xa1r\xb8\x90U\x8a\x02\x1fB\xdc\xdcX\xe3\x9c\x00\x00\u07d4\xeee[\xb4\xee\x0e\x8dTxRo\xb9\xf1^@d\xe0\x9f\xf3\u0749\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xeeiY\xde+g\x96{q\x94\x8c\x89\x1a\xb0\r\x8c\x8f8\xc7\u0709\x06hZ\xc1\xbf\xe3,\x00\x00\u07d4\xeel\x03B\x99i\xca\x12b\xcb?\nJT\xaf\xa7\xd3H\xd7\xf5\x89\r\xe2\x19\xf9\x1f\xc1\x8a\x00\x00\u07d4\xeeqy>:\xcf\x12\xa7'OV9a\xf57R\x9d\x89\xc7\u0789lk\x93[\x8b\xbd@\x00\x00\u07d4\xeer\x88\xd9\x10\x86\xd9\xe2\xeb\x91\x00\x14\u066b\x90\xa0-x\u00a0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xee|=\xed|(\xf4Y\xc9/\xe1;M\x95\xba\xfb\xab\x026}\x89%\xf2s\x93=\xb5p\x00\x00\xe0\x94\xee\x86} \x91k\xd2\xe9\xc9\xec\xe0\x8a\xa0C\x85\xdbf|\x91.\x8a\n\x96\x81c\xf0\xa5{@\x00\x00\u07d4\ue25b\x02\xcb\xcb99\xcda\xde\x13B\xd5\x04\x82\xab\xb6\x852\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xee\x90m}_\x17H%\x81t\xbeL\xbc8\x93\x03\x02\xab{B\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\ue5ea\x8a\u019e\xdfz\x98}mp\x97\x9f\x8e\xc1\xfb\xcaz\x94\x89\x14b\fW\xdd\xda\xe0\x00\x00\u07d4\xee\xa1\xe9y\x88\xdeu\xd8!\xcd(\xadh\"\xb2,\u0398\x8b1\x89\x1c0s\x1c\xec\x03 \x00\x00\xe0\x94\xee\u048c?\x06\x8e\tJ0K\x85<\x95\nh\t\xeb\xcb\x03\xe0\x8a\x03\xa9\u057a\xa4\xab\xf1\xd0\x00\x00\u07d4\xee\u04c4\xef-A\xd9\xd2\x03\x97NW\xc1#(\xeav\x0e\b\xea\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xdflB\x80\xe6\xeb\x05\xb94\xac\xe4(\xe1\x1dB1\xb5\x90[\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xe7a\x84~3\xfda\u0653\x87\xee\x14b\x86\x94\u047f\xd5%\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xee\xe9\xd0Rn\xda\x01\xe41\x16\xa3\x952-\u0689pW\x8f9\x8a\x02\x1e\x19\x99\xbb\xd5\u04be\x00\x00\u07d4\xee\xf1\xbb\xb1\xe5\xa8?\u0782H\xf8\x8e\xe3\x01\x8a\xfa-\x132\xeb\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xee\xfb\xa1-\xfc\x99gB\xdby\x04d\xca}';\xe6\xe8\x1b>\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xee\xfd\x05\xb0\xe3\xc4\x17\xd5[3C\x06\x04\x86\xcd\xd5\xe9*\xa7\xa6\x89M\x85<\x8f\x89\b\x98\x00\x00\u07d4\xef\r\xc7\xddzS\xd6\x12r\x8b\xcb\u04b2|\x19\xddM}fo\x89&A\x1c[5\xf0Z\x00\x00\u07d4\xef\x11RR\xb1\xb8E\u0345\u007f\x00-c\x0f\x1bo\xa3zNP\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xef\x1c\x04w\xf1\x18M`\xac\u02b3t\xd3tUz\n>\x10\xf3\x89\b=lz\xabc`\x00\x00\u07d4\xef,4\xbbH}7b\xc3\u0327\x82\xcc\xddz\x8f\xbb\n\x991\x89\t\xc2\x00vQ\xb2P\x00\x00\u07d4\xef5\xf6\u0531\a^j\xa19\x15\x1c\x97K/FX\xf7\x058\x89<;\xc3?\x94\xe5\r\x80\x00\u07d4\xef9\u0291s\xdf\x15S\x1ds\xe6\xb7*hKQ\xba\x0f+\xb4\x89V\xa0\xb4un\xe28\x00\x00\u07d4\xefF<&y\xfb'\x91d\xe2\f=&\x915\x87s\xa0\xad\x95\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xefG\xcf\a>6\xf2q\xd5\"\xd7\xfaNq \xadP\a\xa0\xbc\x89\x87\x86x2n\xac\x90\x00\x00\u07d4\xefa\x15[\xa0\t\xdc\u07be\xf1\v(\xd9\xda=\x1b\xc6\xc9\xce\u0509\x034-`\xdf\xf1\x96\x00\x00\u0794\xefix\x1f2\xff\xce34o,\x9a\xe3\xf0\x84\x93\xf3\xe8/\x89\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xefv\xa4\u034f\xeb\xcb\u0278\x18\xf1x(\xf8\xd94s\xf3\xf3\u02c9\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\uf4c1\x8fhM\xb0\xc3g^\xc8\x132\xb3\x18>\xcc(\xa4\x95\x89T\x06\x923\xbf\u007fx\x00\x00\xe0\x94\xef\x9fY\xae\xdaA\x8c\x14\x94h-\x94\x1a\xabI$\xb5\xf4\x92\x9a\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\uf9b1\xf0\xdb`57\x82h\x91\xb8\xb4\xbc\x169\x84\xbb@\u03495e\x9e\xf9?\x0f\xc4\x00\x00\u07d4\xef\xbdR\xf9}\xa5\xfd:g:F\xcb\xf30D{~\x8a\xad\\\x89\x05l<\x9b\x80\xa0\xa6\x80\x00\xe0\x94\xef\xc8\xcf\x19c\u0269Rg\xb2(\xc0\x86#\x98\x89\xf4\xdf\xd4g\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xef\u02ae\x9f\xf6M,\xd9[RI\xdc\xff\xe7\xfa\xa0\xa0\xc0\xe4M\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xef\xcc\xe0k\xd6\b\x9d\x0eE\x8e\xf5a\xf5\xa6\x89H\n\xfep\x00\x89 \x86\xac5\x10R`\x00\x00\u07d4\xef\xe0g]\xa9\x8a]\xdap\u0356\x19k\x87\xf4\xe7&\xb43H\x89?\x19\xbe\xb8\xdd\x1a\xb0\x00\x00\u07d4\xef\xe8\xff\x87\xfc&\x0e\agc\x8d\xd5\xd0/\xc4g.\x0e\xc0m\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xeb\x19\x97\xaa\xd2w\xcc3C\x0ea\x11\xed\tCY@H\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xef\xee\xa0\x10uo\x81\xdaK\xa2[r\x17\x87\xf0X\x17\v\uff49\x01\u009c\x9c\xf7p\xef\x00\x00\u07d4\xef\xf5\x1dr\xad\xfa\xe1C\xed\xf3\xa4+\x1a\xecU\xa2\xcc\xdd\v\x90\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xef\xf8kQ#\xbc\xdc\x17\xedL\xe8\xe0[~\x12\xe5\x13\x93\xa1\xf7\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xef\xfc\x15\u41f1\xbe\xda\n\x8d\x13%\xbd\xb4\x17\"@\xdcT\n\x89\x03\x8599\xee\xe1\xde\x00\x00\xe0\x94\xf0\x11\x95\xd6W\xef<\x94.l\xb89I\xe5\xa2\v\\\xfa\x8b\x1e\x8a\x05ts\xd0]\xab\xae\x80\x00\x00\u07d4\xf0'\x96)Q\x01gB\x88\xc1\xd94g\x05=\x04\"\x19\xb7\x94\x89(\x1d\x90\x1fO\xdd\x10\x00\x00\u07d4\xf09h={=\"[\xc7\xd8\u07ed\xefc\x164A\xbeA\xe2\x89\x01\xdd\x1eK\xd8\xd1\xee\x00\x00\u07d4\xf0Jj7\x97\b\xb9B\x8dr*\xa2\xb0kw\xe8\x895\u03c9\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf0M,\x91\xef\xb6\xe9\xc4_\xfb\xe7KCL\x8c_+\x02\x8f\x1f\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf0W\xaaf\xcav~\xde\x12J\x1c[\x9c\xc5\xfc\x94\xef\v\x017\x89p\xa2K\u02b6\xf4]\x00\x00\u07d4\xf0[\xa8\u05f6\x859\xd930\v\xc9(\x9c=\x94t\xd0A\x9e\x89\x06\xda'\x02M\xd9`\x00\x00\u07d4\xf0\\\xee\xabeA\x05dp\x99Qw<\x84E\xad\x9fN\u01d7\x89\x10C\x16'\xa0\x93;\x00\x00\xe0\x94\xf0_\xcdL\rs\xaa\x16~US\xc8\xc0\xd6\xd4\xf2\xfa\xa3\x97W\x8a\x02\xd2\xd6l1p\xb2\x98\x00\x00\u07d4\xf0g\xe1\xf1\u0583UjL\xc4\xfd\f\x03\x13#\x9f2\xc4\xcf\u060965\u026d\xc5\u07a0\x00\x00\u07d4\xf0g\xfb\x10\u07f2\x93\u962b\xe5d\xc0U\xe34\x8f\x9f\xbf\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0h\xdf\xe9]\x15\xcd:\u007f\x98\xff\xa6\x88\xb44hB\xbe&\x90\x89D\n\xd8\x19\xe0\x97L\x00\x00\xe0\x94\xf0j\x85J<]\xc3m\x1cI\xf4\xc8}m\xb33\xb5~J\u074a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf0y\xe1\xb1&_P\xe8\u0229\x8e\xc0\u01c1^\xb3\xae\xac\x9e\xb4\x89\x01\x16\xdc:\x89\x94\xb3\x00\x00\xe0\x94\xf0{\xd0\xe5\xc2\xcei\xc7\u0127$\xbd&\xbb\xfa\x9d*\x17\xca\x03\x8a\x01@a\xb9\xd7z^\x98\x00\x00\xe0\x94\xf0\x83*k\xb2U\x03\xee\xcaC[\xe3\x1b\v\xf9\x05\xca\x1f\xcfW\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf0\x9b>\x87\xf9\x13\xdd\xfdW\xae\x80I\xc71\u06e9\xb66\xdf\u00c9 \xf5\xb1\uab4d\x80\x00\x00\u07d4\xf0\xb14\v\x99oo\v\xf0\xd9V\x1c\x84\x9c\xaf\u007fD0\xbe\xfa\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xb1\xf9\xe2x2\xc6\xdei\x14\xd7\n\xfc#\x8ct\x99\x95\xac\xe4\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf0\xb4i\xea\xe8\x9d@\f\xe7\xd5\xd6j\x96\x95\x03p6\xb8\x89\x03\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf0\xb9\u0583\u03a1+\xa6\x00\xba\xac\xe2\x19\xb0\xb3\xc9~\x8c\x00\xe4\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf0\xbe\x0f\xafMy#\xfcDF\"\u0458\f\xf2\u0650\xaa\xb3\a\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xc0\x81\xdaR\xa9\xae6d*\xdf^\b _\x05\xc5Ah\xa6\x89\x06\x04o7\xe5\x94\\\x00\x00\u07d4\xf0\xc7\r\rm\xabvc\xaa\x9e\xd9\xce\xeaV~\xe2\u01b0'e\x89qC\x8a\u0167\x91\xa0\x80\x00\u07d4\xf0\xcb\xef\x84\xe1ic\x00\x98\xd4\xe3\x01\xb2\x02\b\xef\x05\x84j\u0249\x0e\v\x83EPkN\x00\x00\u07d4\xf0\xd2\x16c\u0630\x17n\x05\xfd\xe1\xb9\x0e\xf3\x1f\x850\xfd\xa9_\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf0\xd5\xc3\x1c\xcbl\xbe0\xc7\xc9\xea\x19\xf2h\xd1Y\x85\x1f\x8c\x9c\x8a\x03\x89O\x0eo\x9b\x9fp\x00\x00\u07d4\xf0\xd6L\xf9\xdf\tt\x113\xd1pH_\xd2K\x00P\x11\xd5 \x89\x1b\b\x93A\xe1O\xcc\x00\x00\u07d4\xf0\xd8X\x10^\x1bd\x81\x01\xac?\x85\xa0\xf8\"+\xf4\xf8\x1dj\x89 \x86\xac5\x10R`\x00\x00\u07d4\xf0\xdcC\xf2\x05a\x91'P{+\x1c\x1c\xfd\xf3-(1\t \x89\x10^\xb7\x9b\x94\x17\b\x80\x00\u07d4\xf0\xe1\u07e4*\u07ac/\x17\xf6\xfd\xf5\x84\xc9Hb\xfdV3\x93\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf0\xe2d\x9c~j?,]\xfe3\xbb\xfb\xd9'\xca<5\nX\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf0\xe7\xfb\x9eB\nS@\xd56\xf4\x04\b4O\xea\xef\xc0j\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf1\x04b\xe5\x8f\xcc\a\U000d5121\x87c\x94Q\x16~\x85\x92\x01\x89\t4\xdd]3\xbc\x97\x00\x00\xe0\x94\xf1\x06a\xff\x94\x14\x0f >zH%rCy8\xbe\xc9\xc3\xf7\x8a\x04<3\xc1\x93ud\x80\x00\x00\u0794\xf1\x14\xff\r\x0f$\xef\xf8\x96\xed\xdeTq\u07a4\x84\x82J\x99\xb3\x88\xbe -j\x0e\xda\x00\x00\u07d4\xf1\x16\xb0\xb4h\x0fS\xabr\xc9h\xba\x80.\x10\xaa\x1b\xe1\x1d\u0209\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xf1\x1c\xf5\xd3cto\xeehd\xd3\xca3m\xd8\x06y\xbb\x87\xae\x8a\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf1\x1e\x01\u01e9\xd1$\x99\x00_M\xaew\x16\tZ4\x17bw\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf1;\b0\x93\xbaVN-\xc61V\x8c\xf7T\r\x9a\x0e\xc7\x19\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf1O\x0e\xb8m\xb0\xebhu?\x16\x91\x8e]K\x80t7\xbd>\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf1Qx\xff\xc4:\xa8\a\x0e\xce2~\x93\x0f\x80\x9a\xb1\xa5O\x9d\x89\n\xb6@9\x12\x010\x00\x00\u07d4\xf1V\xdc\v*\x98\x1e[U\xd3\xf2\xf0;\x814\xe31\u06ed\xb7\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf1]\x9dZ!\xb1\x92\x9ey\x03q\xa1\u007f\x16\xd9_\fie\\\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1^\x18,O\xbb\xady\xbd\x934\"B\xd4\xdc\xcf+\xe5\x89%\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf1bM\x98\ve3o\xea\u0166\xd5A%\x00\\\xfc\xf2\xaa\u02c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1g\xf5\x86\x8d\xcfB3\xa7\x83\x06\th,\xaf-\xf4\xb1\xb8\a\x89\x81\xe5B\xe1\xa78?\x00\x00\u07d4\xf1m\xe1\x89\x1d\x81\x96F\x13\x95\xf9\xb16&[;\x95F\xf6\xef\x89\x01\xb2\x8e\x1f\x98\xbb\u0380\x00\u07d4\xf1z\x92\xe06\x1d\xba\xce\xcd\xc5\xde\r\x18\x94\x95Z\xf6\xa9\xb6\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1z\xdbt\x0fE\u02fd\xe3\tN~\x13qo\x81\x03\xf5c\xbd\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x8b\x14\xcb\xf6iC6\xd0\xfe\x12\xac\x1f%\xdf-\xa0\xc0]\xbb\x89\xd8\xd4`,&\xbfl\x00\x00\u07d4\xf1\x9b98\x9dG\xb1\x1b\x8a,?\x1d\xa9\x12M\xec\xff\xbe\xfa\xf7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf1\x9f\x195\b9>M*\x12{ \xb2\x03\x1f9\xc8%\x81\u0189\xbd\xbdz\x83\xbd/l\x00\x00\u07d4\xf1\xa1\xf3 @yd\xfd<\x8f.,\u0224X\r\xa9O\x01\xea\x89ll!wU|D\x00\x00\u07d4\xf1\xb4\xec\xc65%\xf7C,=\x83O\xfe+\x97\x0f\xbe\xb8r\x12\x89\xa2\xa2@h\xfa\u0340\x00\x00\u07d4\U000753ef\xfa\x87\x94\xf5\n\xf8\xe8\x83\t\u01e6&TU\xd5\x1a\x8963\x03\"\xd5#\x8c\x00\x00\u07d4\xf1\xc8\u0129A\xb4b\x8c\rl0\xfd\xa5dR\u065c~\x1bd\x89N\x8c\xea\x1e\xdeu\x04\x00\x00\u07d4\xf1\xda@so\x99\xd5\xdf;\x06\x8a]t_\xaf\xc6F?\u0271\x89\x06\x96\xca#\x05\x8d\xa1\x00\x00\u07d4\xf1\u070a\xc8\x10B\xc6z\x9c<g\x92\xb20\xc4j\xc0\x16\xca\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf1\xdfU\xdc\xc3J\x05\x10\x12\xb5u\u02d6\x8b\xc9\xc4X\xea\t\u0249\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf1\xe9\x80\xc5Y\xa1\xa8\xe5\xe5\nG\xf8\xff\xfd\xc7s\xb7\xe0jT\x8a\x06_\xfb\xcd\xea\x04\xb7H\x00\x00\xe0\x94\xf1\xf3\x91\u0292\x80\x88\x17\xb7U\xa8\xb8\xf4\xe2\xca\b\xd1\xfd\x11\b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf1\xf7f\xb0\xe4ms\xfc\xd4\xd5.zr\xe1\xb9\x19\f\xc62\xb3\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xf2\x04\x952\xfdE\x8a\x83\xca\x1b\xff.\ubb36\xd5\xcac\xf4\xa4\x89\u010c\x99\x1d\xc1T\\\x80\x00\u07d4\xf2\x06\xd3(\xe4q\xd0\x11{$m*F\x19\x82w\t\xe9m\U000c98af=\xc0\x05CD\x00\x00\u07d4\xf2\f\x9a\x99\xb7GY\u05c2\xf2\\\x1c\xec\xa8\x02\xa2~\vCl\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\xe0\x94\xf2\x12}T\x18\x8f\xed\xef\x0f3\x8a_8\xc7\xffs\xad\x9foB\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf2\x1341\xd1\u0663{\xa2\xf0v+\xc4\fZ\u030a\xa6\x97\x8e\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf2\x15I\xbd\xd1Hy\x12\xf9\x00\xa7R=\xb5\xf7ba!\xbb\xa3\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf2\x18\xbd\x84\x8e\xe7\xf9\u04cb\xfd\xd1\xc4\xeb.\xd2Ij\xe40_\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf2$\xeb\x90\v7\xb4I\x0e\xeej\vd \xd8\\\x94}\x873\x894\x95tD\xb8@\xe8\x00\x00\u07d4\xf2)J\u06f6\xf0\xdc\xc7nc.\xbe\U0010ad1f\x12M\xbb\xa4\x89NC96\x00\xa7\xb1\x00\x00\u07d4\xf2/@x\xfe\xbb\xba\xa8\xb0\xe7\x8ed,\x8aB\xf3]C9\x05\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf27\xef\x05&\x1c4\u05dc\xc2+\x86\r\xe0\xf1\u007fy<8`\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf2<{\f\xb8\xcdY\xb8+\u0610dJW\xda\xf4\f\x85\xe2x\x89\x02\xb6j\xaf\xe3&\xff\x00\x00\u07d4\xf2=\x01X\x9e\xb1-C\x9ftH\xffT0u)\U0005114d\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf2>\\c2!\xa8\xf76>e\x87\f\x9f(t$\u04a9`\x89J\xcfX\xe0rW\x10\x00\x00\u07d4\xf2B\u0684]B\u053fw\x9a\x00\xf2\x95\xb4\aP\xfeI\xea\x13\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2RY\xa5\xc99\xcd%\x96l\x9bc\x03\xd3s\x1cS\u077cL\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf2^Lp\xbcFV2\u021eV%\xa82\xa7r/k\xff\xab\x89\xf3K\x82\xfd\x8e\x91 \x00\x00\u07d4\xf2k\xce\xdc\xe3\xfe\xad\u03a3\xbc>\x96\xeb\x10@\xdf\xd8\xff\u1809*\x03I\x19\u07ff\xbc\x00\x00\u07d4\xf2py%v\xf0]QD\x93\xff\xd1\xf5\xe8K\xecK-\xf8\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf2s,\xf2\xc1;\x8b\xb8\xe7I*\x98\x8f_\x89\xe3\x82s\xdd\u0209 \x86\xac5\x10R`\x00\x00\xe0\x94\xf2t.hY\xc5i\xd5\xf2\x10\x83Q\xe0\xbfM\xca5*H\xa8\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf2\x81:d\xc5&]\x02\x025\u02dc1\x9bl\x96\xf9\x06\xc4\x1e\x89\x12\xf99\u025e\u06b8\x00\x00\u07d4\xf2\x87\xffR\xf4a\x11z\xdb>\x1d\xaaq\x93-\x14\x93\xc6_.\x89\xc5S%\xcat\x15\xe0\x00\x00\u07d4\xf2\xab\x11au\x02D\xd0\xec\xd0H\xee\r>Q\xab\xb1C\xa2\xfd\x89B\xfe+\x90ss\xbc\x00\x00\u07d4\xf2\xb4\xab,\x94'\xa9\x01^\xf6\xee\xff\xf5\xed\xb6\x019\xb7\x19\u0449&\u06d9*;\x18\x00\x00\x00\u07d4\xf2\xc0>*8\x99\x8c!d\x87`\xf1\xe5\xae~\xa3\a}\x85\"\x89\x8f?q\x93\xab\a\x9c\x00\x00\u0794\xf2\u0090N\x9f\xa6d\xa1\x1e\xe2VV\xd8\xfd,\xc0\u0665\"\xa0\x88\xb9\x8b\xc8)\xa6\xf9\x00\x00\u07d4\xf2\xc3b\xb0\xef\x99\x1b\xc8/\xb3nf\xffu\x93*\xe8\u0742%\x89\x04\x02\xf4\xcf\xeeb\xe8\x00\x00\u07d4\xf2\xd0\xe9\x86\xd8\x14\xea\x13\xc8\xf4f\xa0S\x8cS\u0712&Q\xf0\x89J\xcfX\xe0rW\x10\x00\x00\xe0\x94\xf2\u04775w$\xecL\x03\x18[\x87\x9bc\xf5~&X\x91S\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf2\xd5v<\xe0s\x12~,\xed\xdeo\xab\xa7\x86\xc7<\xa9AA\x8a\x01\xacB\x86\x10\x01\x91\xf0\x00\x00\xe0\x94\xf2\u055c\x89#u\x90s\xd6\xf4\x15\xaa\xf8\xeb\x06_\xf2\U000f614a\x01\xab,\xf7\xc9\xf8~ \x00\x00\u07d4\xf2\xe9\x9f\\\xbb\x83kz\xd3bGW\x1a0,\xbeKH\x1ci\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf2\xed>w%J\u02c3#\x1d\xc0\x86\x0e\x1a\x11$+\xa6'\u06c9kV\x05\x15\x82\xa9p\x00\x00\xe0\x94\xf2\xed\xde7\xf9\xa8\u00dd\u07a2My\xf4\x01WW\xd0k\xf7\x86\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf2\xef\xe9e`\xc9\xd9{r\xbd6DxC\x88\\\x1d\x90\xc21\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf2\xfb\xb6\u0607\xf8\xb8\xcc:\x86\x9a\xba\x84\u007f=\x1fd<S\u0589\xd8\xc9F\x00c\xd3\x1c\x00\x00\u07d4\xf3\x03Cg\xf8}$\xd3\a\u007f\xa9\xa2\u328b\f\xcb\x11\x04\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf3\x03\u0568\x16\xaf\xfd\x97\xe8=\x9eM\xac/y\a+\xb0\t\x8f\x894\n\xad!\xb3\xb7\x00\x00\x00\u07d4\xf3\x15\x98f\u00bc\x86\xbb\xa4\x0f\x9ds\xbb\x99\xf1\xee\xe5{\xb9\u05c965\u026d\xc5\u07a0\x00\x00\u07d4\xf3\x16\xef\x1d\xf2\xffMl\x18\b\u06e6c\uc013iyh\xe0\x89aFMl\u0700\xf0\x00\x00\xe0\x94\xf3-%\xeb\x0e\xa2\xb8\xb3\x02\x8aLz\x15]\xc1\xaa\xe8exM\x8a\x015\x93\xa9)\u007f\xda\xd6\x00\x00\xe0\x94\xf32\xc0\xf3\xe0Z'\xd9\x12o\u0436A\xa8\xc2\xd4\x06\x06\b\xfd\x8a\x01\x0f\x1bb\xc4\xd9dN\x80\x00\xe0\x94\xf38E\x9f2\xa1Y\xb2=\xb3\n\xc35v\x9a\xb25\x1a\xa6<\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xf3>\xfcc\x97\xaae\xfbS\xa8\xf0z\x0f\x89:\xae0\xe8\xbc\xee\x89|\xf28\x1fa\x9f\x15\x00\x00\u07d4\xf3@\x83\xec\xea8P\x17\xaa@\xbd\xd3^\xf7\xef\xfbL\xe7v-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf3F\xd7\u0792t\x1c\b\xfcX\xa6M\xb5[\x06-\xde\x01-\x14\x89\x0f\xffk\x1fv\x1em\x00\x00\xe0\x94\xf3U\xd3\xec\f\xfb\x90}\x8d\xbb\x1b\xf3FNE\x81(\x19\v\xac\x8a\x01\v\x04n\u007f\r\x80\x10\x00\x00\u07d4\xf3m\xf0/\xbd\x89`sG\xaf\xce)i\xb9\xc4#jX\xa5\x06\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3s\xe9\u06ac\f\x86u\xf5;yz\x16\x0fo\xc04\xaek#\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3{BeG\xa1d-\x8032H\x14\xf0\xed\xe3\x11O\xc2\x12\x89\x15\xbeat\xe1\x91.\x00\x00\u07d4\xf3{\xf7\x8cXu\x15G\x11\xcbd\r7\xeam(\xcf\xcb\x12Y\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf3\x82\xdfX1U\xd8T\x8f?\x93D\f\xd5\xf6\x8c\xb7\x9d`&\x8a8u}\x02\u007f\xc1\xfd\\\x00\x00\xe0\x94\xf3\x82\xe4\xc2\x04\x10\xb9Q\b\x9e\x19\xba\x96\xa2\xfe\xe3\xd9\x1c\xce~\x8a\x01\x11\xfaV\xee\u00a88\x00\x00\xe0\x94\xf3\x8al\xa8\x01hS~\x97M\x14\xe1\xc3\xd19\x90\xa4L,\x1b\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\u07d4\xf3\x9a\x9dz\xa3X\x1d\xf0~\xe4'\x9a\xe6\xc3\x12\xef!\x036X\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xb6h\xb3\xf1M\x92\x0e\xbc7\x90\x92\u06d8\x03\x1bg\xb2\x19\xb3\x89\n\xd6\xee\xdd\x17\xcf;\x80\x00\u07d4\U000fe679\x10<\xe7U\n\xa7O\xf1\xdb\x18\xe0\x9d\xfe2\xe0\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xc1\xab\u049d\xc5{A\xdc\x19-\x0e8M\x02\x1d\xf0\xb4\xf6\u0509\x97\xae\f\u07cf\x86\xf8\x00\x00\u07d4\xf3\xc4qm\x1e\xe5'\x9a\x86\xd0\x16:\x14a\x81\x81\xe1a6\u01c965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf3\u030b\xcbU\x94e\xf8\x1b\xfeX;\u05eb\n#\x06E;\x9e\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xf3\u0588\xf0k\xbd\xbfP\xf9\x93,AE\xcb\xe4\x8e\xcd\xf6\x89\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf3\xdb\xcf\x13Z\u02dd\xee\x1aH\x9cY<\x02O\x03\u00bb\xae\u0389lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xde_&\xefj\xde\xd6\xf0m;\x91\x13F\xeep@\x1d\xa4\xa0\x89\x13:\xb3}\x9f\x9d\x03\x00\x00\u07d4\xf3\xdfc\xa9q\x99\x93308;>\xd7W\v\x96\u0101#4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf3\xe7OG\f}:?\x003x\x0fv\xa8\x9f>\xf6\x91\xe6\u02c9\xa3\xcf\xe61\xd1Cd\x00\x00\u07d4\xf3\xeb\x19H\xb9Q\xe2-\xf1ax)\xbf;\x8d\x86\x80\xeckh\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf3\xf1\xfa9\x18\xca4\xe2\xcf~\x84g\v\x1fM\x8e\xca\x16\r\xb3\x89$\xdc\xe5M4\xa1\xa0\x00\x00\u07d4\xf3\xf2O\u009e @?\xc0\xe8\xf5\xeb\xbbU4&\xf7\x82p\xa2\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf3\xfar5R\xa5\xd0Q.+b\xf4\x8d\xca{+\x81\x050[\x89\amA\xc6$\x94\x84\x00\x00\u07d4\xf3\xfeQ\xfd\xe3D\x13\xc73\x18\xb9\xc8T7\xfe~\x82\x0fV\x1a\x896b2\\\u044f\xe0\x00\x00\u07d4\xf4\x00\xf9=_\\~?\xc3\x03\x12\x9a\xc8\xfb\f/xd\a\xfa\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\v\x13O\xea\"\u01b2\x9c\x84W\xf4\x9f\x00\x0f\x9c\xdax\x9a\u06c9 \x86\xac5\x10R`\x00\x00\u07d4\xf4\x15W\xdf\u07f1\xa1\xbd\xce\xfe\xfe.\xba\x1e!\xfe\nJ\x99B\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\x17z\r\x85\u050b\x0e&B\x11\xce*\xa2\xef\xd3\xf1\xb4\u007f\b\x89\xc2\xcc\xca&\xb7\xe8\x0e\x80\x00\u07d4\xf4/\x90R1\xc7p\xf0\xa4\x06\xf2\xb7h\x87\u007f\xb4\x9e\xee\x0f!\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf42\xb9\u06ef\x11\xbd\xbds\xb6Q\x9f\xc0\xa9\x04\x19\x87q\xaa\u0189\b=lz\xabc`\x00\x00\u07d4\xf4=\xa3\xa4\xe3\xf5\xfa\xb1\x04\u029b\xc1\xa0\xf7\xf3\xbbJV\xf3Q\x89lj\xccg\u05f1\xd4\x00\x00\xe0\x94\xf4G\x10\x8b\x98\xdfd\xb5~\x87\x103\x88\\\x1a\xd7\x1d\xb1\xa3\xf9\x8a\x01v\xf4\x9e\xad4\x83P\x80\x00\u07d4\xf4O\x85Q\xac\xe93r\a\x12\xc5\u0111\u0376\xf2\xf9Qsl\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u0794\xf4V\x05Z\x11\xab\x91\xfff\x8e.\xc9\"\x96\x1f*#\xe3\xdb%\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf4V\xa7[\xb9\x96U\xa7A,\xe9}\xa0\x81\x81m\xfd\xb2\xb1\xf2\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4[\x1d\xcb.A\xdc'\xff\xa0$\u06ad\xf6\x19\xc1\x11u\xc0\x87\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xf4c\xa9\f\xb3\xf1>\x1f\x06CB66\xbe\xab\x84\xc1#\xb0m\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf4h\x90n~\xdffJ\xb0\u063e=\x83\xebz\xb3\xf7\xff\xdcx\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xf4i\x80\u3929\u049ajn\x90`E7\xa3\x11K\xcb(\x97\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf4kk\x9c|\xb5R\x82\x9c\x1d=\xfd\x8f\xfb\x11\xaa\xba\xe7\x82\xf6\x89\x01#n\xfc\xbc\xbb4\x00\x00\u07d4\xf4v\xe1&\u007f\x86$|\xc9\b\x81o.z\xd58\x8c\x95-\xb0\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf4v\xf2\xcbr\b\xa3.\x05\x1f\xd9N\xa8f)\x92c\x82\x87\xa2\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xf4{\xb14\xda0\xa8\x12\xd0\x03\xaf\x8d\u0338\x88\xf4K\xbfW$\x8a\x01\x19Y\xb7\xfe3\x95X\x00\x00\u07d4\xf4\x83\xf6\a\xa2\x1f\xcc(\x10\n\x01\x8cV\x8f\xfb\xe1@8\x04\x10\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf4\x8e\x1f\x13\xf6\xafM\x84\xb3q\xd7\xdeK'=\x03\xa2c'\x8e\x89 \x86\xac5\x10R`\x00\x00\xe0\x94\xf4\x9cG\xb3\xef\xd8knj[\xc9A\x8d\x1f\x9f\xec\x81Ki\xef\x8a\x04<3\xc1\x93ud\x80\x00\x00\xe0\x94\xf4\x9fo\x9b\xaa\xbc\x01\x8c\x8f\x8e\x11\x9e\x01\x15\xf4\x91\xfc\x92\xa8\xa4\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf4\xa3g\xb1f\u0499\x1a+\xfd\xa9\xf5dc\xa0\x9f%,\x1b\x1d\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\xa5\x1f\xceJ\x1d[\x94\xb0q\x83\x89\xbaNx\x14\x13\x9c\xa78\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xf4\xa9\xd0\f\xef\xa9{zX\xef\x94\x17\xfcbg\xa5\x06\x909\xee\x89\x01.\x89(\u007f\xa7\x84\x00\x00\u07d4\xf4\xaa\xa3\xa6\x16>7\x06W{I\xc0v~\x94\x8ah\x1e\x16\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\xb1bn$\xf3\v\xca\xd9'<R\u007f\xccqK]\x00{\x8f\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf4\xb4\x91\x00uwr\xf3<\x17{\x9av\xba\x95\"l\x8f=\u060a\x01k5-\xa5\xe0\xed0\x00\x00\xe0\x94\xf4\xb6\xcd\xcf\xcb$#\v3}w\r\xf6\x03M\xfb\xd4\xe1P?\x8a\x04\x05\xfd\xf7\u5bc5\xe0\x00\x00\u07d4\xf4\xb7Y\u030a\x1cu\xf8\bI\xeb\xbc\u0687\x8d\xc8\xf0\xd6m\xe4\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf4\xbajF\xd5Q@\xc49\xcb\xcf\al\xc6W\x13bb\xf4\xf8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf4\xd6z\x90D\xb45\xb6n\x89w\xff9\xa2\x8d\u013dSr\x9a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4\xd9vd\xccN\xec\x9e\xdb\xe7\xfa\t\xf4u\nf;P}y\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf4\xdc{\xa8T\x80\xbb\xb3\xf55\xc0\x95h\xaa\xa3\xafo7!\u018a\x01\x87\x1f\xb60~5\xe5\x00\x00\xe0\x94\xf4\xeb\xf5\v\xc7\xe5O\x82\u9e7d$\xba\xef)C\x8e%\x9c\xe6\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf4\uc397\xa2\n\xa5\xf8\xdd oU ~\x06\xb8\x13\xdf,\xc0\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf4\ud10e\xc9as\x9c,~5/C[\xa7\n|\xd5\xdb8\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf4\xfcM9\xbc\f,@h\xa3m\xe5\x0eJ\xb4\xd4\xdb~4\n\x89\x01`7\u07c7\xefj\x00\x00\xe0\x94\xf5\x04\x94:\xaf\x16yn\v4\x1b\xbc\xdf!\xd1\x1c\u0146\xcd\u044a\x01\xe7\xe4\x17\x1b\xf4\u04e0\x00\x00\u07d4\xf5\x06\x1e\xe2\xe5\xee&\xb8\x15P6w\x13\x0e\x1d\xe0zR\xdb\a\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf5\tU~\x90\x18?\xbf\x0f\x06Q\xa7\x86H{\xccB\x8b\xa1u\x89\n\x84Jt$\xd9\xc8\x00\x00\xe0\x94\xf5\n\xbb\u052aE\xd3\xeb\x88QTe\xa8\xba\v1\x0f\u0675!\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf5\n\xe7\xfa\xb4\u03f5\xa6F\xee\x04\u03ad\xf9\xbf\x9d\u0568\xe5@\x89\xd8\xd6|/X\x95H\x00\x00\u07d4\xf5\f\xba\xfd9~\xddUl\x06x\x98\x8c\xb2\xaf\\&\x17\u0889&\xd0~\xfex+\xb0\x00\x00\xe0\x94\xf5\x1f\xde\xd8\n\xcbP(\x90\xe8sit\x1f7\"QL\xef\xff\x8a\x04<4V\xca<m\x11\x00\x00\xe0\x94\xf5*X\x82\xe8\x92}\x94K5\x9b&6k\xa2\xb9\xca\u03fa\xe8\x8a\x05KA\xce/\xe6;\xa8\x00\x00\xe0\x94\xf5,\nxw4_\xe0\xc23\xbb\x0f\x04\xfdj\xb1\x8bo\x14\xba\x8aT\xcb\xe5Y\x89\xf3\x8d\xe0\x00\x00\u07d4\xf5C~\x15\x80\x90\xb2\xa2\u058f\x82\xb5JXd\xb9]\xd6\xdb\xea\x89\xd9l\x16p;+\xfe\x00\x00\xe0\x94\xf5L\x19\xd9\xef8s\xbf\xd1\xf7\xa6\"\xd0-\x86$\x9a2\x8f\x06\x8a\t`\xae\x12z\xf3/\xb2\x80\x00\u07d4\xf5P\x01x\u02d9\x8f\x12d\x17\x83\x1a\b\xc2\u05eb\xff\xf6\xab_\x89F\xf4\xf4\xa5\x87Z\x9f\x80\x00\u07d4\xf5SH\x15\xdcc^\xfa\\\xc8K*\xc74r>!\xb2\x93r\x89U\xa6\xe7\x9c\xcd\x1d0\x00\x00\u07d4\xf5U\xa2{\xb1\xe2\xfdN,\u01c4\xca\ue493\x9f\xc0n/\u0249lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5X\xa2\xb2\xdd&\u0755\x93\xaa\xe0E1\xfd<<\u00c5Kg\x89\n\xbb\xcdN\xf3wX\x00\x00\u07d4\xf5`H\xdd!\x81\u0523od\xfc\xec\xc6!T\x81\xe4*\xbc\x15\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5dB\xf6\x0e!i\x13\x95\u043f\xfa\xa9\x19M\xca\xff\x12\u2dc9\x0e\x189\x8ev\x01\x90\x00\x00\u07d4\xf5yqJE\xeb\x8fR\xc3\xd5{\xbd\xef\xd2\xc1[./\x11\u07c9T\x91YV\xc4\t`\x00\x00\u07d4\xf5\x93\xc6R\x85\xeek\xbdf7\U000fe3c9\xad@\u0509\xf6U\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf5\x98\xdb.\t\xa8\xa5\xee}r\r+\\C\xbb\x12m\x11\xec\u0089\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xf5\x9d\xab\x1b\xf8\xdf\x112~a\xf9\xb7\xa1KV:\x96\xec5T\x8a\x01EB\xba\x12\xa37\xc0\x00\x00\xe0\x94\xf5\x9f\x9f\x02\xbb\u024e\xfe\t~\xab\xb7\x82\x10\x97\x90!\x89\x8b\xfd\x8a\x02\x1e\x17\x1a>\xc9\xf7,\x00\x00\u07d4\xf5\xa5E\x9f\xcd\xd5\xe5\xb2s\x83\r\xf8\x8e\xeaL\xb7}\xda\u07f9\x89\x04\t\xe5+H6\x9a\x00\x00\u07d4\xf5\xa7gj\xd1H\xae\x9c\x1e\xf8\xb6\xf5\xe5\xa0\xc2\xc4s\xbe\x85\v\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf5\xb0h\x98\x9d\xf2\x9c%5w\xd0@Z\xden\x0eu(\xf8\x9e\x89WG=\x05\u06ba\xe8\x00\x00\u07d4\xf5\xb6\xe9\x06\x1aN\xb0\x96\x16\aw\xe2gb\xcfH\xbd\u0635]\x89\r\xc5_\xdb\x17d{\x00\x00\u07d4\xf5\xcf\xfb\xbabN~\xb3!\xbc\x83\xc6\f\xa6\x81\x99\xb4\xe3fq\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf5\xd1ER\xb1\xdc\xe0\xd6\xdc\x1f2\r\xa6\xff\u02231\xcdo\f\x89Hz\x9a0E9D\x00\x00\xe0\x94\xf5\xd6\x1a\xc4\u0295G^[{\xff\xd5\xf2\xf6\x90\xb3\x16u\x96\x15\x8a\x06\x92\xae\x88\x97\b\x1d\x00\x00\x00\u07d4\xf5\xd9\xcf\x00\xd6X\xddEQzH\xa9\xd3\xf5\xf63T\x1aS=\x89\x06O_\xdfIOx\x00\x00\u07d4\xf5\xea\xdc\xd2\u0478ez\x12\x1f3\xc4X\xa8\xb1>v\xb6U&\x89\r\x8b\x0fZZ\xc2J\x00\x00\u07d4\xf6\a\xc2\x15\r>\x1b\x99\xf2O\xa1\xc7\xd5@\xad\xd3\\N\xbe\x1e\x89\xa7\xf1\xaa\a\xfc\x8f\xaa\x00\x00\u07d4\xf6\v\xd75T>k\xfd.\xa6\xf1\x1b\xffbs@\xbc\x03Z#\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6\f\x1bE\xf1d\xb9X\x0e 'Z\\9\xe1\xd7\x1e5\xf8\x91\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xf6\x0fb\xd797\x95?\xef5\x16\x9e\x11\xd8r\xd2\xea1~\xec\x8a\x01!\xeah\xc1\x14\xe5\x10\x00\x00\u07d4\xf6\x12\x83\xb4\xbd\x85\x04\x05\x8c\xa3`\u94d9\x9bb\xcb\xc8\xcdg\x89\r\xd2\xd5\xfc\xf3\xbc\x9c\x00\x00\u07d4\xf6\x17\xb9g\xb9\xbdH_v\x95\xd2\xefQ\xfbw\x92\u0618\xf5\x00\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf6\x18\u0671\x04A\x14\x80\xa8c\xe6#\xfcU#-\x1aOH\xaa\x89\x0eh\x9emD\xb1f\x80\x00\u07d4\xf6\"\u5126b>\xaa\xf9\x9f+\xe4\x9eS\x80\xc5\xcb\xcf\\\u0609\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf62\xad\xffI\r\xa4\xb7-\x126\xd0KQ\x0ft\xd2\xfa\xa3\u0349K\xe4\xe7&{j\xe0\x00\x00\u07d4\xf69\xac1\u069fg'\x1b\xd1\x04\x02\xb7eN\\\xe7c\xbdG\x89\x15\xaf\x0fB\xba\xf9&\x00\x00\u07d4\xf6:W\x9b\xc3\xea\u00a9I\x04\x10\x12\x8d\xbc\xeb\xe6\xd9\u0782C\x89P\xc5\xe7a\xa4D\b\x00\x00\u07d4\xf6E\xdd|\x89\x00\x93\xe8\xe4\u022a\x92\xa6\xbb55\"\xd3\u0718\x89\aC\x9f\xa2\t\x9eX\x00\x00\xe0\x94\xf6H\xea\x89\xc2u%q\x01r\x94Ny\xed\xff\x84x\x03\xb7u\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xf6JJ\xc8\xd5@\xa9(\x9ch\xd9`\xd5\xfb|\xc4Zw\x83\x1c\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf6N\xcf!\x17\x93\x1cmSZ1\x1eO\xfe\xae\xf9\u0514\x05\xb8\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf6O\xe0\x93\x9a\x8d\x1e\xea*\x0e\u035a\x970\xfdyX\xe31\t\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xf6V\x16\xbe\x9c\x8by~t\x15\"|\x918\xfa\xa0\x89\x17B\u05c9*\xd3s\xcef\x8e\x98\x00\x00\u07d4\xf6W\xfc\xbeh.\xb4\xe8\xdb\x15.\u03c9$V\x00\vQ=\x15\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf6X\x19\xacL\xc1L\x13\u007f\x05\xddyw\xc7\xda\xe0\x8d\x1aJ\xb5\x89\x05\x87\x88\u02d4\xb1\xd8\x00\x00\u07d4\xf6{\xb8\xe2\x11\x8b\xbc\u0550'fn\xed\xf6\x94>\xc9\xf8\x80\xa5\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x84d\xbfd\xf2A\x13V\xe4\xd3%\x0e\xfe\xfe\\P\xa5\xf6[\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf6\x86x[\x89r\va\x14_\ua017\x8dj\u030e\v\xc1\x96\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf6\x8c^3\xfa\x97\x13\x9d\xf5\xb2\xe68\x86\xce4\xeb\xf3\u45dc\x89\xb3\xfaAi\xe2\xd8\xe0\x00\x00\u07d4\xf6\xa8cWW\xc5\xe8\xc14\xd2\r\x02\x8c\xf7x\u03c6\t\xe4j\x89O\x1dw/\xae\xc1|\x00\x00\u07d4\xf6\xb7\x82\xf4\xdc\xd7E\xa6\xc0\xe2\xe00`\x0e\x04\xa2K%\xe5B\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\xe0\x94\xf6\xbc7\xb1\u04a3x\x8dX\x9bm\xe2\x12\xdc\x17\x13\xb2\xf6\u738a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xf6\xc3\u010a\x1a\xc0\xa3G\x99\xf0M\xb8n\u01e9u\xfewh\xf3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf6\xd2]?=\x84m#\x9fR_\xa8\xca\xc9{\xc45x\u06ec\x890\x92\u007ft\xc9\xde\x00\x00\x00\u07d4\xf6\xea\xacp2\u0512\xef\x17\xfd`\x95\xaf\xc1\x1dcOV\xb3\x82\x89\x1b\x1bk\u05efd\xc7\x00\x00\xe0\x94\xf6\xea\xd6}\xbf[~\xb13X\xe1\x0f6\x18\x9dS\xe6C\xcf\u03ca\bxg\x83&\xea\xc9\x00\x00\x00\u07d4\xf6\xf1\xa4C\t\x05\x1ck%\xe4}\xff\x90\x9b\x17\x9b\xb9\xabY\x1c\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xf7\x03(\xef\x97b_\xe7E\xfa\xa4\x9e\xe0\xf9\u052a;\r\xfbi\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\n\x99\x8aq{3\x8d\x1d\u0658T@\x9b\x1a3\x8d\ue930\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\rcz\x84\\\x06\xdbl\u0711\xe67\x1c\xe7\xc48\x8ab\x8e\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x15R\x13D\x98\x92tK\xc6\x0f.\x04@\a\x88\xbd\x04\x1f\u0749\x03\x9f\xba\xe8\xd0B\xdd\x00\x00\xe0\x94\xf7\x1bE4\xf2\x86\xe40\x93\xb1\xe1^\xfe\xa7I\xe7Y{\x8bW\x8a\x16\x1c\x13\xd34\x1c\x87(\x00\x00\u07d4\xf74\xec\x03rM\xde\xe5\xbbRy\xaa\x1a\xfc\xf6\x1b\f\xb4H\xa1\x89\xe5\xbf,\u0270\x97\x80\x00\x00\u07d4\xf76\u0716v\x00\x128\x8f\xe8\x8bf\xc0n\xfeW\xe0\xd7\xcf\n\x89q\xd7Z\xb9\xb9 P\x00\x00\u07d4\xf7:\xc4l ;\xe1S\x81\x11\xb1Q\xec\x82 \u01c6\xd8AD\x89\x0f\xf77x\x17\xb8+\x80\x00\u07d4\xf7=\xd9\xc1B\xb7\x1b\xce\x11\xd0n0\xe7\xe7\xd02\xf2\uc71e\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf7A\x8a\xa0\xe7\x13\xd2H\"\x87v\xb2\xe7CB\"\xaeu\u3949lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7Nn\x14S\x82\xb4\u06c2\x1f\xe0\xf2\u0643\x88\xf4V\t\u019f\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xf7P\f\x16o\x8b\xea/\x824v\x06\xe5\x02K\xe9\xe4\xf4\u0399\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7W\xfc\x87 \xd3\xc4\xfaRw\a^`\xbd\\A\x1a\xeb\xd9w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7[\xb3\x9cy\x97y\xeb\xc0J3m&\r\xa61F\xed\x98\u0409\x01Z\xf1\u05cbX\xc4\x00\x00\xe0\x94\xf7h\xf3!\xfdd3\xd9kO5M<\xc1e,\x172\xf5\u007f\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf7oi\xce\xe4\xfa\xa0\xa6;0\xae\x1ex\x81\xf4\xf7\x15ep\x10\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf7w6\x1a=\u062bb\xe5\xf1\xb9\xb0GV\x8c\xc0\xb5UpL\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf7|{\x84QI\xef\xba\x19\xe2a\xbc|u\x15y\b\xaf\xa9\x90\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf7\u007f\x95\x87\xffz-r\x95\xf1\xf5q\u0206\xbd3\x92jR|\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xf7\x82X\xc1$\x81\xbc\xdd\u06f7*\x8c\xa0\xc0C\tra\xc6\u0149\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\x98\xd1m\xa4\xe4`\xc4`\xcdH_\xae\x0f\xa0Y\x97\b\ub08965\u026d\xc5\u07a0\x00\x00\u07d4\xf7\xa1\xad\xe2\xd0\xf5)\x12=\x10U\xf1\x9b\x17\x91\x9fV!Ng\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf7\xac\xff\x93K\x84\xda\ti\xdc7\xa8\xfc\xf6C\xb7\xd7\xfb\xedA\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xf7\xb1Q\xcc^W\x1c\x17\xc7e9\xdb\xe9\x96L\xbbo\xe5\xdey\x89tq|\xfbh\x83\x10\x00\x00\u07d4\xf7\xb2\x9b\x82\x19\\\x88-\xabx\x97\u00ae\x95\xe7w\x10\xf5xu\x89w5Aa2\xdb\xfc\x00\x00\u07d4\xf7\xbcLD\x91\rZ\xed\xd6n\xd25U8\xa6\xb1\x93\xc3a\xec\x89\x05A\xde,-\x8db\x00\x00\u07d4\xf7\xc0\f\xdb\x1f\x02\x03\x10\u056c\xab{Ij\xaaD\xb7y\b^\x89Z\x87\xe7\xd7\xf5\xf6X\x00\x00\u07d4\xf7\xc1\xb4C\x96\x8b\x11{]\u0677UW/\xcd9\xca^\xc0K\x89\x18\xb9h\u0092\xf1\xb5\x00\x00\xe0\x94\xf7\xc5\x0f\x92*\xd1ka\xc6\u047a\xa0E\xed\x81h\x15\xba\u010f\x8a\x02\xa99j\x97\x84\xad}\x00\x00\u07d4\xf7\xc7\b\x01Pq\xd4\xfb\n:*\t\xa4]\x15c\x96\xe34\x9e\x89\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf7\xcb\u06e6\xbel\xfeh\xdb\xc2<+\x0f\xf50\xee\x05\"o\x84\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf7\xd0\xd3\x10\xac\xea\x18@a8\xba\xaa\xbb\xfe\x05q\xe8\r\xe8_\x89Hz\x9a0E9D\x00\x00\u07d4\xf7\u05ef LV\xf3\x1f\xd9C\x98\xe4\r\xf1\x96K\u063f\x12<\x89\b!\xd2!\xb5)\x1f\x80\x00\u07d4\xf7\xdc%\x11\x96\xfb\u02f7|\x94}|\x19F\xb0\xffe\x02\x1c\xea\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf7\xe4Z\x12\xaaq\x1cp\x9a\xce\xfe\x95\xf3;xa-*\xd2*\x8a\x0e\x06U\xe2\xf2k\xc9\x18\x00\x00\u07d4\xf7\xf4\x89\x8cLRm\x95_!\xf0U\xcbnG\xb9\x15\xe5\x19d\x89|\b`\xe5\xa8\r\xc0\x00\x00\u07d4\xf7\xf9\x1ez\xcb[\x81)\xa3\x06\x87|\xe3\x16\x8eoC\x8bf\xa1\x89\t\x8a}\x9b\x83\x14\xc0\x00\x00\u07d4\xf7\xfcE\xab\xf7oP\x88\xe2\u5d68\xd12\xf2\x8aMN\xc1\xc0\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\x06:\xf4\xcc\x1d\xd9a\x9a\xb5\u063f\xf3\xfc\xd1\xfa\xa8H\x82!\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\bnBf\x1e\xa9)\xd2\u0761\xablt\x8c\xe3\x05]\x11\x1e\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xf8\bw\x86\xb4-\xa0N\xd6\xd1\xe0\xfe&\xf6\xc0\xee\xfe\x1e\x9fZ\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xf8\r6\x19p/\xa5\x83\x8cH9\x18Y\xa89\xfb\x9c\xe7\x16\x0f\x89l\a\xa7\u0471np\x00\x00\u07d4\xf8\x14y\x9fm\xdfM\xcb)\xc7\xee\x87\x0eu\xf9\xcc-52m\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xf8\x15\xc1\n\x03-\x13\xc3K\x89v\xfan;\xd2\xc9\x13\x1a\x8b\xa9\x89Hz\x9a0E9D\x00\x00\u07d4\xf8\x16\"\xe5WW\xda\xeafu\x97]\xd958\xda}\x16\x99\x1e\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8$\xee3\x1eJ\xc3\xccXv\x939[W\xec\xf6%\xa6\xc0\u0089V\xc9]\xe8\xe8\xca\x1d\x00\x00\u07d4\xf8'\xd5n\xd2\xd3' \u052b\xf1\x03\xd6\xd0\xefM;\xcdU\x9b\x89\x01l\x80\x06W\x91\xa2\x80\x00\u07d4\xf8)\x85\x91R>P\xb1\x03\xf0\xb7\x01\xd6#\xcb\xf0\xf7EV\xf6\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8H\xfc\xe9\xaba\x1c}\x99 n#\xfa\u019a\u0508\xb9O\xe1\x89\x02\xa1\x12\x9d\t6r\x00\x00\u07d4\xf8O\t\n\xdf?\x8d\xb7\u1533P\xfb\xb7u\x00i\x9ff\xfd\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf8Q\xb0\x10\xf63\xc4\n\xf1\xa8\xf0js\ubeabe\az\xb5\x89\xee\x86D/\xcd\x06\xc0\x00\x00\u07d4\xf8X\x17\x1a\x04\xd3W\xa1;IA\xc1n~U\xdd\u0514\x13)\x89\x02F\xa5!\x8f*\x00\x00\x00\u07d4\xf8[\xab\x1c\xb3q\x0f\xc0_\xa1\x9f\xfa\xc2.gR\x1a\v\xa2\x1d\x89l\x955\u007f\xa6\xb3l\x00\x00\u07d4\xf8j>\xa8\a\x1fp\x95\xc7\u06ca\x05\xaePz\x89)\u06f8v\x89\x126\xef\xcb\u02f3@\x00\x00\u07d4\xf8pL\x16\xd2\xfd[\xa3\xa2\xc0\x1d\x0e\xb2\x04\x84\xe6\xec\xfa1\t\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf8p\x99_\xe1\xe5\"2\x1duC7\xa4\\\f\x9d{8\x95\x1c\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf8s\xe5ze\xc9;n\x18\xcbu\xf0\xdc\a}[\x893\xdc\\\x89\n\xad\xec\x98?\xcf\xf4\x00\x00\u07d4\xf8ua\x9d\x8a#\xe4]\x89\x98\u0444\u0500\xc0t\x89p\x82*\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8{\xb0{(\x9d\xf70\x1eT\xc0\xef\xdaj,\xf2\x91\xe8\x92\x00\x89K\xe4\xe7&{j\xe0\x00\x00\u0794\xf8\x89\x00\xdbsyU\xb1Q\x9b\x1a}\x17\n\x18\x86L\xe5\x90\xeb\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xf8\x8bX\xdb7B\vFL\v\xe8\x8bE\xee+\x95)\x0f\x8c\xfa\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf8\x96+u\xdb]$\xc7\xe8\xb7\xce\xf1\x06\x8c>g\u03bb0\xa5\x89\x0f-\xc7\xd4\u007f\x15`\x00\x00\u07d4\xf8\xa0e\xf2\x87\xd9\x1dw\xcdbj\xf3\x8f\xfa\"\r\x9bU*+\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xf8\xa4\x9c\xa29\f\x1fm\\\x0ebQ;\a\x95qt?|\u0189\xa2\xa1]\tQ\x9b\xe0\x00\x00\u07d4\xf8\xa5\f\xee.h\x8c\xee\u3b24\u0522\x97%\xd4\a,\u0103\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xacJ9\xb5<\x110x \x97;D\x13e\xcf\xfeYof\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xae\x85{g\xa4\xa2\x89:?\xbe|z\x87\xff\x1c\x01\u01a6\xe7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xf8\xbf\x9c\x04\x87NZw\xf3\x8fL8R~\x80\xc6v\xf7\xb8\x87\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xc7\xf3J8\xb3\x18\x01\xdaC\x064w\xb1+'\xd0\xf2\x03\xff\x89\x1a\u04ba\xbao\xefH\x00\x00\u07d4\xf8\xca3l\x8e\x91\xbd \xe3\x14\xc2\v-\xd4`\x8b\x9c\x8b\x94Y\x89-\u071b\u0173,x\x00\x00\u07d4\xf8\xd1t$\xc7g\xbe\xa3\x12\x05s\x9a+W\xa7'r\x14\uef89\x02F\xdd\xf9yvh\x00\x00\u07d4\xf8\xd5-\xcc_\x96\xcc(\x00{>\u02f4\t\xf7\xe2*dl\xaa\x89\b\x16\x90\xe1\x81(H\x00\x00\u07d4\xf8\xdc\xe8g\xf0\xa3\x9c[\xef\x9e\xeb\xa6\t\"\x9e\xfa\x02g\x8bl\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf8\xf2&\x14*B\x844\xab\x17\xa1\x86J%\x97\xf6J\xab/\x06\x89\tY\x8b/\xb2\xe9\xf2\x80\x00\u07d4\xf8\xf6d^\r\xeedK=\xad\x81\xd5q\uf6ef\x84\x00!\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x01\xc0\x0f\xc1\u06c8\xb6\x9cK\xc3%+\\\xa7\x0e\xa6\xee\\\xf6\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9=[\xcb\x06D\xb0\xcc\xe5\xfc\u0763C\xf5\x16\x8f\xfa\xb2\x87}\x89\vb\a\xb6}&\xf9\x00\x00\u07d4\xf9W\x0e\x92L\x95\u07bbpa6\x97\x92\xcf.\xfe\u00a8-^\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xf9d \x86\xb1\xfb\xaea\xa6\x80M\xbe_\xb1^\xc2\u04b57\xf4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9d\x88i\x85\x90\xdc;,UVB\xb8q4\x8d\xfa\x06z\u0549\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9d\u064d(\x170\xba5\xb2\xe3\xa3\x14yn{B\xfe\xdfg\x89S\xb0\x87`\x98\xd8\f\x00\x00\u07d4\xf9e\ri\x89\xf1\x99\xab\x1c\xc4ycm\xed0\xf2A\x02\x1fe\x89.\x14\x1e\xa0\x81\xca\b\x00\x00\xe0\x94\xf9h\x83X$Y\x90\x8c\x82v'\xe8o(\xe6F\xf9\xc7\xfcz\x8a\x01\u0127\x877\xcd\u03f8\x00\x00\u07d4\xf9kL\x00voSsj\x85t\xf8\"\xe6GL/!\xda-\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9r\x9dH(,\x9e\x87\x16m^\xef-\x01\xed\xa9\xdb\xf7\x88!\x89\x05k\x83\xdd\xc7(T\x80\x00\u07d4\xf9v~N\xcbJY\x80Ru\b\u05fe\xc3\xd4^Ld\x9c\x13\x89g\x8a\x93 b\xe4\x18\x00\x00\xe0\x94\xf9x\xb0%\xb6B3U\\\xc3\xc1\x9a\xda\u007fA\x99\xc94\x8b\xf7\x8aT\xb4\v\x1f\x85+\xda\x00\x00\x00\u07d4\xf9{V\xeb\u0577z\xbc\x9f\xba\u02eb\u0514\xb9\xd2\xc2!\xcd\x03\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xf9\x81\x1f\xa1\x9d\xad\xbf\x02\x9f\x8b\xfeV\x9a\xdb\x18\"\x8c\x80H\x1a\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xf9\x82Ps\fLa\xc5\u007f\x12\x985\xf2h\b\x94yEB\xf3\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xf9\x894gr\x99^\xc1\x90o\xaf\xfe\xba*\u007f\xe7\u079ck\xab\x8a\x01je\x02\xf1Z\x1eT\x00\x00\u07d4\xf9\x98\xca4\x11s\nl\xd1\x0etU\xb0A\x0f\xb0\xf6\xd3\xff\x80\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\x9a\xeeDKW\x83\xc0\x93\xcf\xff\xd1\xc4c,\xf9<o\x05\f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xf9\x9e\xee\xce9\xfa~\xf5\am\x85Pa8@\ty,\xf2\xe0\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9\xa5\x9c<\xc5\xff\xac\xbc\xb6{\xe0\xfcrV\xf6L\x9b\x12|\xb4\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xf9\xa9K\xd5a\x98\xda$^\xd0\x1d\x1ed0\xb2K'\b\xdc\xc0\x89(\xa7z\xfd\xa8~\xe5\x00\x00\u07d4\xf9\xb3x%\xf00s\xd3\x1e$\x93x\xc3\fy\\3\xf8:\xf2\x89\n\u066a\xbf\x8c\x9b\xfc\x00\x00\u07d4\xf9\xb6\x17\xf7R\xed\xec\xae>\x90\x9f\xbb\x91\x1d/\x81\x92\xf8B\t\x89\x90\xf54`\x8ar\x88\x00\x00\u07d4\xf9\xbf\xb5\x9dS\x8a\xfcHt\xd4\xf5\x94\x1b\b\xc9s\x0e8\xe2K\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xf9\xdd#\x90\b\x18/\xb5\x19\xfb0\xee\xdd \x93\xfe\xd1c\x9b\xe8\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xf9\u07ba\xec\xb5\xf39\xbe\xeaH\x94\xe5 K\xfa4\r\x06\u007f%\x89ZB\x84Fs\xb1d\x00\x00\xe0\x94\xf9\xe3tG@lA!\x97\xb2\u2bbc\x00\x1dn0\u024c`\x8a\x01\xc4y\xbbCI\xc0\xee\x00\x00\u07d4\xf9\xe7\"/\xaa\xf0\xf4\xda@\xc1\u0124\x0607:\t\xbe\u05f6\x89\x9bO\u0730\x94V$\x00\x00\u07d4\xf9\xec\xe0\"\xbc\xcd,\x924i\x11\xe7\x9d\xd5\x03\x03\xc0\x1e\x01\x88\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfa\x00\xc3v\xe8\x9c\x05\u81c1z\x9d\xd0t\x8d\x96\xf3A\xaa\x89\x89\x10M\r\x00\u04b7\xf6\x00\x00\u07d4\xfa\f\x1a\x98\x8c\x8a\x17\xad5(\xeb(\xb3@\x9d\xaaX\"_&\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa\x10_\x1a\x11\xb6\xe4\xb1\xf5`\x12\xa2y\"\xe2\xac-\xa4\x81/\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfa\x14/\xe4~\u0697\xe6P;8k\x18\xa2\xbe\xdds\u0335\xb1\x89.\x15:\xd8\x15H\x10\x00\x00\u07d4\xfa\x14\xb5f#J\xbe\xe70B\xc3\x1d!qq\x82\u02e1J\xa1\x89\x11\xc7\xea\x16.x \x00\x00\u07d4\xfa\x19\xd6\xf7\xa5\x0fO\a\x98\x93\xd1g\xbf\x14\xe2\x1d\x00s\u0456\x89\x1c\xbb:?\xf0\x8d\b\x00\x00\u07d4\xfa\x1f\x19q\xa7u\xc3PO\xefPy\xf6@\xc2\u013c\xe7\xac\x05\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfa'\x9b\xfd\x87g\xf9V\xbf\u007f\xa0\xbdV`\x16\x8d\xa7V\x86\xbd\x89\x90\xf54`\x8ar\x88\x00\x00\xe0\x94\xfa'\xccI\xd0\vl\x98s6\xa8u\xae9\xdaX\xfb\x04\x1b.\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfa(2\x99`=\x87X\xe8\u02b0\x82\x12],\x8f}DT)\x8a\x01[\xca\xcb\x1e\x05\x01\xae\x80\x00\u07d4\xfa+\xbc\xa1]?\u37ca2\x8e\x91\xf9\r\xa1Oz\xc6%=\x89\n\u05ce\xbcZ\xc6 \x00\x00\xe0\x94\xfa/\u049d\x03\xfe\xe9\xa0x\x93\xdf:&\x9fV\xb7/.\x1ed\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfa3U2\x85\xa9sq\x9a\r_\x95o\xf8a\xb2\u061e\xd3\x04\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\fK\x90?n\xa5.\xa7\xab{\x88c\xb6\xa6\x16\xadfP\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfa:\x1a\xa4H\x8b5\x1a\xa7V\f\xf5\xeec\n/\xd4\\2\"\x89/\xa4~j\xa74\r\x00\x00\u07d4\xfaA\tq\xad\"\x9c06\xf4\x1a\u03c5/*\u0259(\x19P\x89\u0633\x11\xa8\xdd\xfa|\x00\x00\u07d4\xfaD\xa8U\xe4\x04\xc8m\f\xa8\xef3$%\x1d\xfb4\x9cS\x9e\x89T\"S\xa1&\xce@\x00\x00\xe0\x94\xfaR\x01\xfe\x13B\xaf\x110{\x91B\xa0A$<\xa9./\t\x8a 8\x11j:\xc0C\x98\x00\x00\xe0\x94\xfa`\x86\x8a\xaf\xd4\xffL\\W\x91K\x8e\u054bBWs\u07e9\x8a\x01\xcf\xe5\xc8\b\xf3\x9f\xbc\x00\x00\u07d4\xfag\xb6{O7\xa0\x15\t\x15\x11\x0e\xde\a;\x05\xb8S\xbd\xa2\x89#\x19\xba\x94sq\xad\x00\x00\u07d4\xfah\xe0\xcb>\xdfQ\xf0\xa6\xf2\x11\u0272\xcb^\a<\x9b\xff\xe6\x89\x0f\xc969(\x01\xc0\x00\x00\xe0\x94\xfaj7\xf0\x18\xe9yg\x93\u007f\xc5\xe8a{\xa1\u05c6\xdd_w\x8a\x04<0\xfb\b\x84\xa9l\x00\x00\u07d4\xfav\x06C[5l\xee%{\xd2\xfc\xd3\xd9\xea\xcb<\xd1\xc4\xe1\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfaz\xdff\v\x8d\x99\xce\x15\x93=|_\a/<\xbe\xb9\x9d3\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfa\x86\xca'\xbf(T\u0648p\x83\u007f\xb6\xf6\xdf\xe4\xbfdS\xfc\x89\x11u~\x85%\xcf\x14\x80\x00\u07d4\xfa\x8c\xf4\xe6'i\x8c]W\x88\xab\xb7\x88\x04\x17\xe7P#\x13\x99\x89\xe6\x1a6\x96\xee\xf6\x10\x00\x00\u07d4\xfa\x8e;\x1f\x13C9\x00s}\xaa\xf1\xf6)\x9cH\x87\xf8[_\x89&\u009eG\u0104L\x00\x00\u07d4\xfa\x9e\xc8\xef\xe0\x86\x86\xfaX\xc1\x813Xr\xbai\x85`\ucac9lj\xccg\u05f1\xd4\x00\x00\u07d4\xfa\xad\x90]\x84|{#A\x8a\xee\xcb\xe3\xad\u06cd\xd3\xf8\x92J\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xfa\xae\xba\x8f\xc0\xbb\xdaU<\xa7.0\xef=s.&\xe8 A\x89H\x8d(*\xaf\xc9\xf6\x80\x00\u07d4\xfa\xb4\x87P\r\xf2\x0f\xb8>\xbe\xd9\x16y\x1dV\x17r\xad\xbe\xbf\x89lkLM\xa6\u077e\x00\x00\u07d4\xfa\xc5\u0294u\x80x\xfb\xfc\xcd\x19\xdb5X\xda~\u8827h\x897(\xa6+\r\xcf\xf6\x00\x00\u07d4\xfa\xd9j\xb6\xacv\x8a\xd5\t\x94R\xacGw\xbd\x1aG\xed\u010f\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xfa\xe7g\x19\xd9~\xacA\x87\x04(\xe9@'\x9d\x97\xddW\xb2\xf6\x8a\x14\u06f2\x19\\\xa2(\x90\x00\x00\u07d4\xfa\u8053pG\x89Zf\f\xf2)v\x0f'\xe6h(\xd6C\x89\t\xdd\xc1\xe3\xb9\x01\x18\x00\x00\u07d4\xfa\xe9,\x13p\xe9\u115a]\xf8;V\xd0\xf5\x86\xaa;@L\x89\x05\u0174\xf3\xd8C\x98\x00\x00\xe0\x94\xfa\xf5\xf0\xb7\xb6\xd5X\xf5\t\r\x9e\xa1\xfb-B%\x9cX`x\x8a\x01Z\xff\xb8B\fkd\x00\x00\xe0\x94\xfb\x12o\x0e\xc7i\xf4\x9d\xce\xfc\xa2\xf2\x00(dQX0\x84\xb8\x8a\x01\x0f\xcb\xc25\x03\x96\xbf\x00\x00\xe0\x94\xfb\x13^\xb1Z\x8b\xacr\xb6\x99\x154*`\xbb\xc0k~\a|\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\"<\x1e\"\xea\xc1&\x9b2\xee\x15jS\x85\x92.\xd3o\xb8\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb7\xcfkO\x81\xa9\xe2\"\xfb\xa2.\x9b\xd2KP\x98\xb73\u03c9\x02\x1auJm\xc5(\x00\x00\u07d4\xfb8`\xf4\x12\x1cC.\xbd\xc8\xecj\x031\xb1\xb7\ty.\x90\x89 \x8c9J\xf1\u0208\x00\x00\u07d4\xfb9\x18\x9a\xf8v\xe7b\xc7\x1dl>t\x18\x93\xdf\"l\xed\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfb:\v\rkjq\x8fo\xc0)*\x82]\xc9$z\x90\xa5\u0409\n\xd6\xdd\x19\x9e\x97[\x00\x00\xe0\x94\xfb?\xa1\xac\b\xab\xa9\xcc;\xf0\xfe\x9dH8 h\x8fe\xb4\x10\x8a\x06ZM\xa2]0\x16\xc0\x00\x00\u07d4\xfb?\xe0\x9b\xb86\x86\x15)\xd7Q\x8d\xa2v5\xf58PV\x15\x89K\xe3\x92\x16\xfd\xa0p\x00\x00\xe0\x94\xfbQ%\xbf\x0f^\xb0\xb6\xf0 \xe5k\xfc/\xdf=@,\t~\x8a\x01@a\xb9\xd7z^\x98\x00\x00\u07d4\xfbU\x18qL\xef\xc3m\x04\x86]\xe5\x91^\xf0\xffG\xdf\xe7C\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb_\xfa\xa0\xf7aW&5x\x91GX\x18\x93\x9d 7\u03d6\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfbh\\\x15\xe49\x96^\xf6&\xbf\r\x83L\u0468\x9f+V\x95\x89\u0556{\xe4\xfc?\x10\x00\x00\u07d4\xfbtK\x95\x1d\tK1\x02b\xc8\xf9\x86\xc8`\u07da\xb1\xdee\x89\x02\xd1\xc5\x15\xf1\xcbJ\x80\x00\u07d4\xfby\xab\u06d2\\U\xb9\xf9\x8e\xfe\xefd\xcf\xc9\xeba\xf5\x1b\xb1\x89a@\xc0V\xfb\n\xc8\x00\x00\u07d4\xfb\x81\x13\xf9M\x91s\xee\xfdZ0s\xf5\x16\x80:\x10\xb2\x86\xae\x89\x04V9\x18$O@\x00\x00\u07d4\xfb\x84,\xa2\xc5\xef\x139\x17\xa26\xa0\u052c@i\x01\x10\xb08\x89\x10\x96\x9ab\xbe\x15\x88\x00\x00\u07d4\xfb\x91\xfb\x1aiUS\xf0\u018e!'m\xec\xf0\xb89\t\xb8m\x89\x05l\x006\x17\xafx\x00\x00\u07d4\xfb\x94s\xcfw\x125\n\x1f\xa09Rs\xfc\x80V\aR\xe4\xfb\x89\x06\xaf!\x98\xba\x85\xaa\x00\x00\xe0\x94\xfb\x94\x9cd\u007f\xdc\xfd%\x14\xc7\u054e1\xf2\x8aS-\x8cX3\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xfb\xa5HmS\xc6\xe2@IBA\xab\xf8~C\xc7`\rA:\x89k\xbfaIIH4\x00\x00\u07d4\xfb\xb1a\xfe\x87_\t)\nK&+\xc6\x01\x10\x84\x8f\r\"&\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfb\xbb\xeb\u03fe#^W\xdd#\x06\xad\x1a\x9e\u0141\xc7\xf9\xf4\x8f\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfb\xc0\x1d\xb5NG\xcd\xc3\xc48iJ\xb7\x17\xa8V\xc2?\xe6\xe9\x8a\x01\xcaqP\xab\x17OG\x00\x00\xe0\x94\xfb\xcf\xccJ{\x0f&\xcf&\xe9\xf33!2\xe2\xfcj#\af\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfb\xe7\x16\"\xbc\xbd1\xc1\xa3iv\xe7\xe5\xf6p\xc0\u007f\xfe\x16\u0789\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfb\xed\xe3,4\x9f3\x00\xefL\xd3;M\xe7\xdc\x18\xe4C\xd3&\x89\xabM\xcf9\x9a:`\x00\x00\u07d4\xfb\xf2\x04\xc8\x13\xf86\xd89b\u01c7\fx\b\xca4\u007f\xd3>\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfb\xf7Y3\xe0\x1bu\xb1T\xef\x06i\ak\xe8\u007fb\xdf\xfa\xe1\x8a\x10\x84cr\xf2I\xd4\xc0\x00\x00\u07d4\xfc\x00\x96\xb2\x1e\x95\xac\xb8\xd6\x19\xd1v\xa4\xa1\xd8\xd5)\xba\xdb\xef\x89\x14\xd9i;\xcb\xec\x02\x80\x00\xe0\x94\xfc\x00\xa4 \xa3a\a\xdf\xd5\xf4\x95\x12\x8a_\u5af2\xdb\x0f4\x8a\x01C\x17\x9d\x86\x91\x10 \x00\x00\xe0\x94\xfc\x01\x8ai\n\xd6tm\xbe:\u03d7\x12\xdd\xcaR\xb6%\x009\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xfc\x02s@3\xe5\u007fpQ~\n\xfc~\xe6$a\xf0o\xad\x8e\x89\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfc\x0e\xe6\xf7\u00b3qJ\xe9\x91lEVf\x05\xb6V\xf3$A\x89_h\xe8\x13\x1e\u03c0\x00\x00\u07d4\xfc\x10\xb7\xa6{2h\xd53\x1b\xfbj\x14\xde\xf5\xeaJ\x16,\xa3\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfc\x15\u02d9\xa8\xd1\x03\v\x12w\n\xdd\x03:y\xee\r\f\x90\x8c\x89\x12\xfa\x00\xbdR\xe6$\x00\x00\u07d4\xfc)R\xb4\u011f\xed\u043c\x05(\xa3\bI^mj\x1cq\u0589lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc,\x1f\x88\x96\x1d\x01\x9c>\x9e\xa30\t\x15.\x06\x93\xfb\xf8\x8a\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\xe0\x94\xfc6\x11\x05\u0750\xf9\xed\xe5fI\x9di\xe9\x13\x03\x95\xf1*\u020aS\xa4\xfe/ N\x80\xe0\x00\x00\u07d4\xfc7/\xf6\x92|\xb3\x96\xd9\xcf)\x805\x00\x11\r\xa62\xbcR\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc9\xbeA\tK\x19\x97\xd2\x16\x9e\x82d\xc2\u00fa\xa6\u025b\u0109lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc=\"k\xb3jX\xf5&V\x88W\xb0\xbb\x12\xd1\t\xec\x93\x01\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfcC\x82\x9a\u01c7\xff\x88\xaa\xf1\x83\xba5*\xad\xbfZ\x15\xb1\x93\x89\u05ac\n+\x05R\xe0\x00\x00\u07d4\xfcI\xc1C\x9aA\u05b3\xcf&\xbbg\xe06R$\xe5\xe3\x8f_\x8966\u05ef^\u024e\x00\x00\u07d4\xfcU\x00\x82Q\x05\xcfq*1\x8a^\x9c;\xfci\u021d\f\x12\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\xe0\x94\xfcf\xfa\xba'\u007fK]\xe6J\xd4^\xb1\x9c1\xe0\f\xed>\u054a\x011\xbe\xb9%\xff\xd3 \x00\x00\xe0\x94\xfc~\"\xa5\x03\xecZ\xbe\x9b\b\xc5\v\xd1I\x99\xf5 \xfaH\x84\x8a\x01ZG}\xfb\xe1\xea\x14\x80\x00\u07d4\xfc\x82\x15\xa0\xa6\x99\x13\xf6*C\xbf\x1c\x85\x90\xb9\xdd\xcd\r\x8d\u06c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xfc\x98\x9c\xb4\x87\xbf\x1a}\x17\xe4\xc1\xb7\u0137\xaa\xfd\xdak\n\x8d\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\xe0\x94\xfc\x9b4td\xb2\xf9\x92\x9d\x80~\x03\x9d\xaeH\xd3\u064d\xe3y\x8a\x02\xf6\xf1\a\x80\xd2,\xc0\x00\x00\u07d4\xfc\xa4;\xbc#\xa0\xd3!\xba\x9eF\xb9)s\\\xe7\xd8\xef\f\x18\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfc\xa7>\xff\x87q\xc0\x10;\xa3\xcc\x1a\x9c%\x94H\xc7*\xbf\v\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xad\xa3\x00(?k\xcc\x13J\x91Eg`\xb0\xd7}\xe4\x10\xe0\x89lk\x93[\x8b\xbd@\x00\x00\xe0\x94\xfc\xbc\\q\xac\xe7\x97AE\v\x01,\xf6\xb8\xd3\xf1}\xb6\x8ap\x8a\x02\x05\xb4\u07e1\xeetx\x00\x00\u07d4\xfc\xbd\x85\xfe\xeajuO\xcf4ID\x9e7\xff\x97\x84\xf7w<\x89\xa7J\xdai\xab\xd7x\x00\x00\xe0\x94\xfc\xc9\u0524&.z\x02z\xb7Q\x91\x10\xd8\x02\u0115\xce\xea9\x8a\x01YQ\x82\"K&H\x00\x00\xe0\x94\xfc\xcd\r\x1e\xce\xe2z\xdd\xea\x95\xf6\x85z\xee\xc8\u01e0K(\xee\x8a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\xe0\x94\xfc\u0434\x82|\xd2\b\xff\xbf^u\x9d\xba\x8c<\xc6\x1d\x8c,<\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfc\xe0\x89c\\\xe9z\xba\xc0kD\x81\x9b\xe5\xbb\n>.\v7\x89\x05\x03\x92\nv0\xa7\x80\x00\u07d4\xfc\xf1\x99\xf8\xb8T\"/\x18.N\x1d\t\x9dN2>*\xae\x01\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfc\xfc:P\x04\xd6xa?\v6\xa6B&\x9a\u007f7\x1c?j\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x19\x1a5\x15}x\x13s\xfbA\x1b\xf9\xf2R\x90\x04|^\xef\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfd\x1f\xaa4{\x0f\u0300L-\xa8l6\xd5\xf1\u044bp\x87\xbb\x89\x02\xd6\xeb$z\x96\xf6\x00\x00\u07d4\xfd\x1f\xb5\xa8\x9a\x89\xa7!\xb8yph\xfb\xc4\u007f>\x9dR\xe1I\x89\f\u0435\x83\u007f\xc6X\x00\x00\u07d4\xfd OOJ\xba%%\xbar\x8a\xfd\xf7\x87\x92\xcb\u07b75\xae\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd'W\xcc5Q\xa0\x95\x87\x8d\x97\x87V\x15\xfe\fj2\xaa\x8a\x89 m\xb1R\x99\xbe\xac\x00\x00\u07d4\xfd(r\u045eW\x85<\xfa\x16\xef\xfe\x93\u0431\xd4{O\x93\xfb\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd))'\x1e\x9d \x95\xa2dv~{\r\xf5.\xa0\xd1\xd4\x00\x89\xa2\xa1\xeb%\x1bZ\xe4\x00\x00\u07d4\xfd7z8Rr\x90\f\xb46\xa3\xbbyb\xcd\xff\xe9?]\xad\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd@$+\xb3Jp\x85^\xf0\xfd\x90\xf3\x80-\xec!6\xb3'\x89h\xa8u\a>)$\x00\x00\xe0\x94\xfdE,9i\xec\xe3\x80\x1cT \xf1\xcd\u02a1\xc7\x1e\xd2=\x8a\x15-\x02\xc7\xe1J\xf6\x80\x00\x00\u07d4\xfdKU\x1fo\xdb\u0366\xc5\x11\xb5\xbb7\"P\xa6\xb7\x83\xe54\x89\x01\x1d\xe1\xe6\xdbE\f\x00\x00\u07d4\xfdK\x98\x95X\xae\x11\xbe\f;6\xe2\xd6\xf2\xa5J\x93C\xca.\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfdM\xe8\xe3t\x8a(\x9c\xf7\xd0`Q}\x9d88\x8d\xb0\x1f\xb8\x89\r\x8drkqw\xa8\x00\x00\u07d4\xfdZc\x15\u007f\x91O\u04d8\uac5c\x13}\xd9U\v\xb7q\\\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xfd`\u04b5\xaf=5\xf7\xaa\xf0\u00d3\x05.y\xc4\xd8#\u0645\x89\x03\x0e\xb5\r.\x14\b\x00\x00\u07d4\xfdhm\xe5?\xa9\u007f\x99c\x9e%hT\x97 \xbcX\x8c\x9e\xfc\x89j\xc5\xc6-\x94\x86\a\x00\x00\u07d4\xfd~\u078fR@\xa0eA\xebi\x9dx,/\x9a\xfb!p\xf6\x89Hz\x9a0E9D\x00\x00\u07d4\xfd\x81+\u019f\xb1p\xefW\xe22~\x80\xaf\xfd\x14\xf8\xe4\xb6\u0489lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\x88\xd1\x14\"\x0f\b\x1c\xb3\xd5\xe1[\xe8\x15*\xb0sfWj\x89\x10CV\x1a\x88)0\x00\x00\u07d4\xfd\x91\x856\xa8\xef\xa6\xf6\xce\xfe\x1f\xa1\x159\x95\xfe\xf5\xe3=;\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfd\x92\x0fr&\x82\xaf\xb5\xafE\x1b\x05D\xd4\xf4\x1b;\x9dWB\x89~R\x05j\x12?<\x00\x00\u07d4\xfd\x95y\xf1\x19\xbb\xc8\x19\xa0+a\u3348\x03\xc9B\xf2M2\x89\x05\xb9~\x90\x81\xd9@\x00\x00\u07d4\xfd\xa0\xce\x153\a\a\xf1\v\xce2\x01\x17- \x18\xb9\xdd\xeat\x89\x02\xd0A\xd7\x05\xa2\xc6\x00\x00\xe0\x94\xfd\xa3\x04(\x19\xaf>f)\x00\xe1\xb9+CX\xed\xa6\xe9%\x90\x8a\x19\a\xa2\x84\u054fc\xe0\x00\x00\u07d4\xfd\xa6\x81\x0e\xa5\xac\x98]o\xfb\xf1\xc5\x11\xf1\xc1B\xed\xcf\xdd\xf7\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfd\xb39D\xf26\x06\x15\xe5\xbe#\x95w\u0221\x9b\xa5-\x98\x87\x89 \x9d\x92/RY\xc5\x00\x00\u07d4\xfd\xbaSY\xf7\xec;\xc7p\xacI\x97]\x84N\xc9qbV\xf1\x8965\u026d\xc5\u07a0\x00\x00\xe0\x94\xfd\xc4\xd4vZ\x94/[\xf9i1\xa9\xe8\xccz\xb8\xb7W\xffL\x8a\x12lG\x8a\x0e>\xa8`\x00\x00\xe0\x94\xfd\xcd]\x80\xb1\x05\x89zW\xab\xc4xev\x8b)\x00RB\x95\x8a\x01Z\xf1\u05cbX\xc4\x00\x00\x00\u0794\xfd\xd1\x19_y}O5q}\x15\xe6\xf9\x81\n\x9a?\xf5T`\x88\xfc\x93c\x92\x80\x1c\x00\x00\u07d4\xfd\xd5\x02\xa7N\x81;\u03e3U\xce\xda<\x17ojhq\xaf\u007f\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfd\u357c\vm\\\xbbL\x1d\x8f\xea>\vK\xffc^\x9d\xb7\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xea\xac*\xcf\x1d\x13\x8e\x19\xf2\xfc?\x9f\xb7E\x92\xe3\ud04a\x89$=M\x18\"\x9c\xa2\x00\x00\u07d4\xfd\xec\xc8-\xdf\xc5a\x92\xe2oV<=h\xcbTJ\x96\xbf\xed\x89\x17\xda:\x04\u01f3\xe0\x00\x00\u07d4\xfd\xf4#C\x01\x9b\v\fk\xf2`\xb1s\xaf\xab~E\xb9\xd6!\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xfd\xf4I\xf1\b\xc6\xfbOZ+\b\x1e\xed~E\u645eM%\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfd\xfda4\xc0J\x8a\xb7\xeb\x16\xf0\x06C\xf8\xfe\xd7\u06aa\ucc89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xfe\x00\xbfC\x99\x11\xa5S\x98-\xb68\x03\x92E\xbc\xf02\xdb\u0709\x15[\xd90\u007f\x9f\xe8\x00\x00\u07d4\xfe\x01n\xc1~\xc5\xf1\x0e;\xb9\x8f\xf4\xa1\xed\xa0E\x15v\x82\xab\x89\x14_T\x02\xe7\xb2\xe6\x00\x00\u07d4\xfe\x0e0\xe2\x14)\rt=\xd3\x0e\xb0\x82\xf1\xf0\xa5\"Z\xdea\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe!\v\x8f\x04\xdcmOv!j\xcf\xcb\u055b\xa8;\xe9\xb60\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\"\xa0\xb3\x88f\x8d\x1a\xe2d>w\x1d\xac\xf3\x8aCB#\u0309\xd8\xdb^\xbd{&8\x00\x00\u07d4\xfe6&\x88\x84_\xa2D\u0300~K\x110\xeb7A\xa8\x05\x1e\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe8'\xd5v0\u03c7a\xd5\x12y{\v\x85\x8eG\x8b\xbd\x12\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfeA\x8bB\x1a\x9cm76\x02y\x04u\xd20>\x11\xa7Y0\x897\b\xba\xed=h\x90\x00\x00\u07d4\xfeBI\x12yP\xe2\xf8\x96\xec\x0e~.=\x05Z\xab\x10U\x0f\x89$=M\x18\"\x9c\xa2\x00\x00\xe0\x94\xfeM\x84\x03!o\xd5qW+\xf1\xbd\xb0\x1d\x00W\x89x\u0588\x8a\x02\x15\xf85\xbcv\x9d\xa8\x00\x00\u07d4\xfeS\xb9I\x89\u0619d\xda aS\x95&\xbb\xe9y\xdd.\xa9\x89h\xa8u\a>)$\x00\x00\u07d4\xfeT\x9b\xbf\xe6G@\x18\x98\x92\x93%8\u06afF\u04b6\x1dO\x89\x02+\x1c\x8c\x12'\xa0\x00\x00\xe0\x94\xfea]\x97\\\b\x87\xe0\xc9\x11>\xc7)\x84 \xa7\x93\xaf\x8b\x96\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xfee\xc4\x18\x8dy\"Wi\td D\xfd\xc5#\x95V\x01e\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfei\u007f\xf2,\xa5G\xbf\xc9^3\xd9`\xda`\\gc\xf3[\x89G\xd4\x11\x9f\xd9`\x94\x00\x00\u07d4\xfej\x89[y\\\xb4\xbf\x85\x90=<\xe0\x9cZ\xa49S\u04ff\x89\xb8Pz\x82\a( \x00\x00\u07d4\xfeo_B\xb6\x19;\x1a\xd1b\x06\u4bf5#\x9dM}\xb4^\x89]\u0212\xaa\x111\xc8\x00\x00\u07d4\xfep\x11\xb6\x98\xbf3q\x13-tE\xb1\x9e\xb5\xb0\x945j\xee\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x80\xe9#-\xea\xff\x19\xba\xf9\x98i\x88:K\xdf\x00\x04\xe5<\x89.b\xf2\ni\xbe@\x00\x00\u07d4\xfe\x8en6eW\r\xffz\x1b\xdaiz\xa5\x89\xc0\xb4\xe9\x02J\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x8f\x1f\u072b\u007f\xbe\u0266\xa3\xfc\xc5\aa\x96\x00P\\6\xa3\x89\x01\x11du\x9f\xfb2\x00\x00\u07d4\xfe\x91\xec\xcf+\xd5f\xaf\xa1\x16\x96\xc5\x04\x9f\xa8Lic\nR\x89i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xfe\x96\xc4\xcd8\x15b@\x1a\xa3*\x86\xe6[\x9dR\xfa\x8a\xee'\x89\x8f\x1d\\\x1c\xae7@\x00\x00\u07d4\xfe\x98\xc6d\xc3\xe4G\xa9^i\xbdX!q\xb7\x17n\xa2\xa6\x85\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xfe\x9a\xd1.\xf0]m\x90&\x1f\x96\xc84\n\x03\x81\x97M\xf4w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\x9c\x0f\xff\xef\xb8\x03\b\x12V\xc0\xcfMfY\xe6\xd3>\xb4\xfb\x89R\xd5B\x80O\x1c\xe0\x00\x00\u07d4\xfe\x9c\xfc;\xb2\x93\u0772\x85\xe6%\xf3X/t\xa6\xb0\xa5\xa6\u0349j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xfe\x9e\x11\x97\u05d7JvH\xdc\u01e01\x12\xa8\x8e\xdb\xc9\x04]\x8a\x01\n\xfc\x1a\xde;N\xd4\x00\x00\xe0\x94\xfe\xac\xa2\xactbK\xf3H\xda\u0258QC\xcf\xd6R\xa4\xbeU\x8a\x05\x89\u007f\u02f0)\x14\b\x80\x00\u07d4\xfe\xad\x18\x03\xe5\xe77\xa6\x8e\x18G-\x9a\xc7\x15\xf0\x99L\u00be\x89\x1b\x1a\xe4\xd6\xe2\xefP\x00\x00\u07d4\xfe\xb8\xb8\xe2\xafqj\xe4\x1f\xc7\xc0K\xcf)T\x01VF\x1ek\x89TQt\xa5(\xa7z\x00\x00\u07d4\xfe\xb9-0\xbf\x01\xff\x9a\x19\x01flUsS+\xfa\a\xee\xec\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xfe\xbc1s\xbc\x90r\x13cT\x00+{O\xb3\xbf\xc5?\"\xf1\x89\x14\x0e\xc8\x0f\xa7\xee\x88\x00\x00\u07d4\xfe\xbdH\xd0\xff\xdb\xd5el\xd5\xe6\x866:a\x14R(\xf2y\x89\x97\xc9\xceL\xf6\xd5\xc0\x00\x00\u07d4\xfe\xbd\x9f\x81\xcfx\xbd_\xb6\u0139\xa2K\xd4\x14\xbb\x9b\xfaLN\x89k\xe1\x0f\xb8\xedn\x13\x80\x00\u07d4\xfe\xc0o\xe2{D\u01c4\xb29n\xc9/{\x92:\xd1~\x90w\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xfe\xc1NT\x85\xde+>\xef^t\xc4aF\u06ceEN\x035\x89\t\xb4\x1f\xbf\x9e\n\xec\x00\x00\u07d4\xfe\xd8Gm\x10\u0544\xb3\x8b\xfag7`\x0e\xf1\x9d5\xc4\x1e\u0609b\xa9\x92\xe5:\n\xf0\x00\x00\u07d4\xfe\xef;n\xab\xc9J\xff\xd31\f\x1cM\x0ee7^\x13\x11\x19\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xfe\xf0\x9dp$?9\xed\x8c\xd8\x00\xbf\x96QG\x9e\x8fJ\xca<\x89\n\u05ce\xbcZ\xc6 \x00\x00\u07d4\xfe\xf3\xb3\u07ad\x1ai&\u051a\xa3+\x12\xc2*\xf5M\x9f\xf9\x85\x8965\u026d\xc5\u07a0\x00\x00\u07d4\xff\v|\xb7\x1d\xa9\xd4\xc1\xean\xcc(\xeb\xdaPLc\xf8/\u04498\x8a\x88]\xf2\xfcl\x00\x00\u07d4\xff\f<w\x98\xe8s=\xd2f\x81R\x89\x1b\xab\x80\xa8\xbe\x95\\\x89\x04YF\xb0\xf9\xe9\xd6\x00\x00\u07d4\xff\f\xb0lB\xe3\u0609H\xe4[\u05f0\xd4\u246e\xfe\xeaQ\x89g\x8a\x93 b\xe4\x18\x00\x00\u07d4\xff\f\xc8\xda\xc8$\xfa$\xfc<\xaa!i\xe6\xe0W\xcfc\x8a\u0589\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xff\x0e/\xec0B\aF~\x1e3\a\xf6L\xbf0\xaf\x8f\xd9\u0349lk\x93[\x8b\xbd@\x00\x00\u07d4\xff\x12\x8fK5[\xe1\xdcJo\x94\xfaQ\r\u007f\x15\xd5<*\xff\x89\x93s\x954\u0486\x80\x00\x00\u07d4\xff\x12\u474e\x06\xaa \xf8\x86)<\v\x98\xed~\xffx\x88\x05\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xff s\b\xce\xd28\xa6\xc0\x1a\xd0!<\xa9\xebDe\xd4%\x90\x89lj\xccg\u05f1\xd4\x00\x00\u07d4\xff&\x13\x830'M\xf4\xe0\xa3\b\x1em\xf7\u0758>\xc6\u73c9lk\x93[\x8b\xbd@\x00\x00\u07d4\xff'&)AH\xb8lx\xa97$\x97\xe4Y\x89\x8e\xd3\xfe\xe3\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xff=\xedz@\u04ef\xf0\u05e8\xc4_\xa6\x13j\xa0C=\xb4W\x89lh\xcc\u041b\x02,\x00\x00\u07d4\xff>\xeeW\xc3Mm\xae\x97\r\x8b1\x11\x17\xc55\x86\xcd5\x02\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xff>\xf6\xba\x15\x1c!\xb5\x99\x86\xaed\xf6\xe8\"\x8b\u0262\xc73\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffA\xd9\xe1\xb4\xef\xfe\x18\u0630\xd1\xf6?\xc4%_\xb4\xe0l=\x89Hz\x9a0E9D\x00\x00\u07d4\xffE\xcb4\xc9(6M\x9c\xc9\u063b\x0074ta\x8f\x06\xf3\x89\x05k\xc7^-c\x10\x00\x00\xe0\x94\xffI\xa7u\x81N\xc0\x00Q\xa7\x95\xa8u\xde$Y.\xa4\x00\u050a*Z\x05\x8f\u0095\xed\x00\x00\x00\u07d4\xffJ@\x8fP\xe9\xe7!F\xa2\x8c\xe4\xfc\x8d\x90'\x1f\x11n\x84\x89j\xcb=\xf2~\x1f\x88\x00\x00\u07d4\xffM\x9c\x84\x84\xc4<B\xff,Z\xb7Y\x99d\x98\xd3#\x99M\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xffO\xc6`i\x04lRVX\xc37\xa9\x17\xf2\u05382\xb4\t\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffQb\xf25M\u0112\xc7_\xd6\xe3\xa1\a&\x86`\xee\xcbG\x89\\(=A\x03\x94\x10\x00\x00\u07d4\xffT[\xbbf\xfb\xd0\x0e\xb5\xe67?\xf4\xe3&\xf5\xfe\xb5\xfe\x12\x89\x01\x15\x8eF\t\x13\xd0\x00\x00\u07d4\xff^~\xe7\xd5\x11H!\xe1Y\u0725\xe8\x1f\x18\xf1\xbf\xff\xbf\xf9\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffa\xc9\xc1\xb7\xa3\u0635;\xba \xb3DfTK{!fD\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xffeQ\x1c\xad\xa2Y&\f\x1d\xdcA\x97N\xca\xec\xd3-cW\x89_h\xe8\x13\x1e\u03c0\x00\x00\xe0\x94\xffxC\xc7\x01\n\xa7\xe6\x15\x19\xb7b\xdf\xe4\x91$\xa7k\x0eN\x8a\u0171y$A+\x9b\xb0\x00\x00\xe0\x94\xffxT\x17V\xab+pn\rp\xb1\x8a\xdbp\x0f\xc4\xf1d=\x8a\t(\x96R\x9b\xad\u0708\x00\x00\u07d4\xff\x83\x85PQ\xee\x8f\xfbp\xb4\x81}\xba2\x11\xed#U\x86\x9d\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xff\x85\x0e;\xe1\xebjMrl\b\xfas\xaa\xd3X\xf3\x97\x06\u0689i*\xe8\x89p\x81\xd0\x00\x00\u07d4\xff\x86\xe5\xe8\xe1[S\x90\x96\x00\xe4\x13\b\u06b7_\x0e$\xe4k\x890\xebP\xd2\xe1@\x80\x00\x00\u07d4\xff\x88\xeb\xac\xc4\x1b6\x87\xf3\x9eKY\xe1YY\x9b\x80\u02e3?\x89\x15\xaf\x1dx\xb5\x8c@\x00\x00\u07d4\xff\x8a,\xa5\xa8\x133\xf1\x99\x98%_ 2V\xe1\xa8\x19\xc0\xaa\x89\f$\x9f\xdd2w\x80\x00\x00\u07d4\xff\x8e\xb0}\xe3\u051d\x9dR\xbb\xe8\xe5\xb2m\xbe\x1d\x16\x0f\xa84\x89\xd8\x14\u0739DS\b\x00\x00\xe0\x94\xff\xa4\xaf\xf1\xa3\u007f\x98K\ng'!I':\xe9\xbdA\u3f0a\x02\x1e\x19\xe0\u027a\xb2@\x00\x00\u07d4\xff\xa6\x96\xec\xbdx~f\xab\xaeO\xe8{c_\a\xcaW\xd8H\x89Hz\x9a0E9D\x00\x00\u07d4\xff\xac=\xb8y\xa6\xc7\x15\x8e\x8d\xec`;@tc\xba\r1\u03c9j\xcb=\xf2~\x1f\x88\x00\x00\xe0\x94\xff\xad=\xd7N,\x1fyj\xc6@\xdeV\u0719\xb4\u01d2\xa4\x02\x8a\x01\x0f\f\xf0d\xddY \x00\x00\u07d4\xff\xb0G&\u07e4\x1a\xfd\xc8\x19\x16\x84\x18a\x04r\x97\r{\xfc\x89\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xff\xb3\xbc\xc3\x19j\x8c<\xb84\xce\xc9L4\xfe\xd3[>\x10T\x89H\xa4<T`/p\x00\x00\u07d4\xff\xb9tg3g\xf5\xc0{\xe5\xfd'\r\u0137\x13\x8b\aMW\x89\x85\xeb\u023d\xb9\x06m\x80\x00\u07d4\xff\xb9\xc7!~ft01\xeb7z\xf6\\w\xdbsY\xdc\u0689\x02+\x1c\x8c\x12'\xa0\x00\x00\u07d4\xff\xbc=\xa08\x1e\xc39\xc1\xc0I\xeb\x1e\xd9\xee4\xfd\u03a6\u0289\xd8\xd7&\xb7\x17z\x80\x00\x00\u07d4\xff\xc5\xfcK~\x8a\x02\x93\xff9\xa3\xa0\xf7\xd6\r&F\xd3zt\x89lk\x93[\x8b\xbd@\x00\x00\u07d4\xff\xc9\xcc0\x94\xb0A\xad\x0e\ao\x96\x8a\r\xe3\xb1g%Xf\x89\x17p\xc1e\v\xee\xe8\x00\x00\u07d4\xff\xd5\x17\x0f\u0468\x11\x8dU\x8eu\x11\xe3d\xb2I\x06\xc4\xf6\xb3\x89\x03A\xd8\xcd'\xf1X\x80\x00\xe0\x94\xff\xd6\u0695\x8e\xec\xbc\x01k\xab\x91\x05\x84@\u04dbA\u01fe\x83\x8a\x04<3\xc1\x93ud\x80\x00\x00\u07d4\xff\xe0\xe9\x97\xf1\x97za_Z1Z\xf4\x13\xfdHi4;\xa0\x89\x05l\xd5_\xc6M\xfe\x00\x00\u07d4\xff\u2375<\x90D\xb4\xec\xd4\x05?\u0474\xb1\rpV\u0188\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xff\xe2\xe2\x8c?\xb7GI\xd7\xe7\x80\u070a]B%8\xe6\xe4Q\x89\r\xbb\x81\xe0[\xc1-\x80\x00\u07d4\xff\xe8\xcb\xc1h\x1e^\x9d\xb7J\x0f\x93\xf8\xed%\x89u\x19\x12\x0f\x89Q\xb1\u04c3\x92a\xac\x00\x00\u07d4\xff\xea\xc00^\xde:\x91R\x95\xec\x8ea\xc7\xf8\x81\x00oDt\x89\x05V\xf6L\x1f\xe7\xfa\x00\x00\u07d4\xff\xec\t\x13\xc65\xba\xca/^W\xa3z\xa9\xfb{l\x9bn&\x89+\xa3\x9e\x82\xed]t\x00\x00\xe0\x94\xff\xf3:;\xd3j\xbd\xbdA'\a\xb8\xe3\x10\xd6\x01\x14T\xa7\xae\x8a\x01\xb1\xaeMn.\xf5\x00\x00\x00\u07d4\xff\xf4\xba\u0556c4y\xa2\xa2\x9f\x9a\x8b?x\xee\xfd\a\xe6\xee\x89\x05k\xc7^-c\x10\x00\x00\u07d4\xff\xf7\xac\x99\xc8\xe4\xfe\xb6\f\x97P\x05K\xdc\x14\xce\x18W\xf1\x81\x8965\u026d\xc5\u07a0\x00\x00" +const testnetAllocData = "\xf9\x03\xa4\u0080\x01\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xc2\x05\x01\xc2\x06\x01\xc2\a\x01\xc2\b\x01\xc2\t\x01\xc2\n\x80\xc2\v\x80\xc2\f\x80\xc2\r\x80\xc2\x0e\x80\xc2\x0f\x80\xc2\x10\x80\xc2\x11\x80\xc2\x12\x80\xc2\x13\x80\xc2\x14\x80\xc2\x15\x80\xc2\x16\x80\xc2\x17\x80\xc2\x18\x80\xc2\x19\x80\xc2\x1a\x80\xc2\x1b\x80\xc2\x1c\x80\xc2\x1d\x80\xc2\x1e\x80\xc2\x1f\x80\xc2 \x80\xc2!\x80\xc2\"\x80\xc2#\x80\xc2$\x80\xc2%\x80\xc2&\x80\xc2'\x80\xc2(\x80\xc2)\x80\xc2*\x80\xc2+\x80\xc2,\x80\xc2-\x80\xc2.\x80\xc2/\x80\xc20\x80\xc21\x80\xc22\x80\xc23\x80\xc24\x80\xc25\x80\xc26\x80\xc27\x80\xc28\x80\xc29\x80\xc2:\x80\xc2;\x80\xc2<\x80\xc2=\x80\xc2>\x80\xc2?\x80\xc2@\x80\xc2A\x80\xc2B\x80\xc2C\x80\xc2D\x80\xc2E\x80\xc2F\x80\xc2G\x80\xc2H\x80\xc2I\x80\xc2J\x80\xc2K\x80\xc2L\x80\xc2M\x80\xc2N\x80\xc2O\x80\xc2P\x80\xc2Q\x80\xc2R\x80\xc2S\x80\xc2T\x80\xc2U\x80\xc2V\x80\xc2W\x80\xc2X\x80\xc2Y\x80\xc2Z\x80\xc2[\x80\xc2\\\x80\xc2]\x80\xc2^\x80\xc2_\x80\xc2`\x80\xc2a\x80\xc2b\x80\xc2c\x80\xc2d\x80\xc2e\x80\xc2f\x80\xc2g\x80\xc2h\x80\xc2i\x80\xc2j\x80\xc2k\x80\xc2l\x80\xc2m\x80\xc2n\x80\xc2o\x80\xc2p\x80\xc2q\x80\xc2r\x80\xc2s\x80\xc2t\x80\xc2u\x80\xc2v\x80\xc2w\x80\xc2x\x80\xc2y\x80\xc2z\x80\xc2{\x80\xc2|\x80\xc2}\x80\xc2~\x80\xc2\u007f\x80\u00c1\x80\x80\u00c1\x81\x80\u00c1\x82\x80\u00c1\x83\x80\u00c1\x84\x80\u00c1\x85\x80\u00c1\x86\x80\u00c1\x87\x80\u00c1\x88\x80\u00c1\x89\x80\u00c1\x8a\x80\u00c1\x8b\x80\u00c1\x8c\x80\u00c1\x8d\x80\u00c1\x8e\x80\u00c1\x8f\x80\u00c1\x90\x80\u00c1\x91\x80\u00c1\x92\x80\u00c1\x93\x80\u00c1\x94\x80\u00c1\x95\x80\u00c1\x96\x80\u00c1\x97\x80\u00c1\x98\x80\u00c1\x99\x80\u00c1\x9a\x80\u00c1\x9b\x80\u00c1\x9c\x80\u00c1\x9d\x80\u00c1\x9e\x80\u00c1\x9f\x80\u00c1\xa0\x80\u00c1\xa1\x80\u00c1\xa2\x80\u00c1\xa3\x80\u00c1\xa4\x80\u00c1\xa5\x80\u00c1\xa6\x80\u00c1\xa7\x80\u00c1\xa8\x80\u00c1\xa9\x80\u00c1\xaa\x80\u00c1\xab\x80\u00c1\xac\x80\u00c1\xad\x80\u00c1\xae\x80\u00c1\xaf\x80\u00c1\xb0\x80\u00c1\xb1\x80\u00c1\xb2\x80\u00c1\xb3\x80\u00c1\xb4\x80\u00c1\xb5\x80\u00c1\xb6\x80\u00c1\xb7\x80\u00c1\xb8\x80\u00c1\xb9\x80\u00c1\xba\x80\u00c1\xbb\x80\u00c1\xbc\x80\u00c1\xbd\x80\u00c1\xbe\x80\u00c1\xbf\x80\u00c1\xc0\x80\u00c1\xc1\x80\u00c1\u0080\u00c1\u00c0\u00c1\u0100\u00c1\u0140\u00c1\u0180\u00c1\u01c0\u00c1\u0200\u00c1\u0240\u00c1\u0280\u00c1\u02c0\u00c1\u0300\u00c1\u0340\u00c1\u0380\u00c1\u03c0\u00c1\u0400\u00c1\u0440\u00c1\u0480\u00c1\u04c0\u00c1\u0500\u00c1\u0540\u00c1\u0580\u00c1\u05c0\u00c1\u0600\u00c1\u0640\u00c1\u0680\u00c1\u06c0\u00c1\u0700\u00c1\u0740\u00c1\u0780\u00c1\u07c0\u00c1\xe0\x80\u00c1\xe1\x80\u00c1\xe2\x80\u00c1\xe3\x80\u00c1\xe4\x80\u00c1\xe5\x80\u00c1\xe6\x80\u00c1\xe7\x80\u00c1\xe8\x80\u00c1\xe9\x80\u00c1\xea\x80\u00c1\xeb\x80\u00c1\xec\x80\u00c1\xed\x80\u00c1\xee\x80\u00c1\xef\x80\u00c1\xf0\x80\u00c1\xf1\x80\u00c1\xf2\x80\u00c1\xf3\x80\u00c1\xf4\x80\u00c1\xf5\x80\u00c1\xf6\x80\u00c1\xf7\x80\u00c1\xf8\x80\u00c1\xf9\x80\u00c1\xfa\x80\u00c1\xfb\x80\u00c1\xfc\x80\u00c1\xfd\x80\u00c1\xfe\x80\u00c1\xff\x80\u3507KT\xa8\xbd\x15)f\xd6?pk\xae\x1f\xfe\xb0A\x19!\xe5\x8d\f\x9f,\x9c\xd0Ft\xed\xea@\x00\x00\x00" +const devAllocData = "\xf9\x01\x94\xc2\x01\x01\xc2\x02\x01\xc2\x03\x01\xc2\x04\x01\xf0\x94\x1a&3\x8f\r\x90^)_\u0337\x1f\xa9\ua11f\xfa\x12\xaa\xf4\x9a\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x94.\xf4q\x00\xe0x{\x91Q\x05\xfd^?O\xf6u y\xd5\u02da\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x94l8jK&\xf7<\x80/4g?rH\xbb\x11\x8f\x97BJ\x9a\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x94\xb9\xc0\x15\x91\x8b\u06ba$\xb4\xff\x05z\x92\xa3\x87=n\xb2\x01\xbe\x9a\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x94\xcd*=\x9f\x93\x8e\x13\u0354~\xc0Z\xbc\u007f\xe74\u07cd\xd8&\x9a\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x94\xdb\xdb\xdb,\xbd#\xb7\x83t\x1e\x8d\u007f\xcfQ\xe4Y\xb4\x97\u499a\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x94\xe4\x15{4\xea\x96\x15\u03fd\xe6\xb4\xfd\xa4\x19\x82\x81$\xb7\fx\x9a\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x94\xe6qo\x95D\xa5lS\r\x86\x8eK\xfb\xac\xb1r1[\u07ad\x9a\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" diff --git a/core/genesis_test.go b/core/genesis_test.go new file mode 100644 index 000000000..4312a80b8 --- /dev/null +++ b/core/genesis_test.go @@ -0,0 +1,161 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package core + +import ( + "math/big" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" +) + +func TestDefaultGenesisBlock(t *testing.T) { + block, _ := DefaultGenesisBlock().ToBlock() + if block.Hash() != params.MainNetGenesisHash { + t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainNetGenesisHash) + } + block, _ = DefaultTestnetGenesisBlock().ToBlock() + if block.Hash() != params.TestNetGenesisHash { + t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestNetGenesisHash) + } +} + +func TestSetupGenesis(t *testing.T) { + var ( + customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50") + customg = Genesis{ + Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)}, + Alloc: GenesisAlloc{ + {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, + }, + } + oldcustomg = customg + ) + oldcustomg.Config = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(2)} + tests := []struct { + name string + fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error) + wantConfig *params.ChainConfig + wantHash common.Hash + wantErr error + }{ + { + name: "genesis without ChainConfig", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + return SetupGenesisBlock(db, new(Genesis)) + }, + wantErr: errGenesisNoConfig, + wantConfig: params.AllProtocolChanges, + }, + { + name: "no block in DB, genesis == nil", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + return SetupGenesisBlock(db, nil) + }, + wantHash: params.MainNetGenesisHash, + wantConfig: params.MainnetChainConfig, + }, + { + name: "mainnet block in DB, genesis == nil", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + DefaultGenesisBlock().MustCommit(db) + return SetupGenesisBlock(db, nil) + }, + wantHash: params.MainNetGenesisHash, + wantConfig: params.MainnetChainConfig, + }, + { + name: "custom block in DB, genesis == nil", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + customg.MustCommit(db) + return SetupGenesisBlock(db, nil) + }, + wantHash: customghash, + wantConfig: customg.Config, + }, + { + name: "custom block in DB, genesis == testnet", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + customg.MustCommit(db) + return SetupGenesisBlock(db, DefaultTestnetGenesisBlock()) + }, + wantErr: &GenesisMismatchError{Stored: customghash, New: params.TestNetGenesisHash}, + wantHash: params.TestNetGenesisHash, + wantConfig: params.TestnetChainConfig, + }, + { + name: "compatible config in DB", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + oldcustomg.MustCommit(db) + return SetupGenesisBlock(db, &customg) + }, + wantHash: customghash, + wantConfig: customg.Config, + }, + { + name: "incompatible config in DB", + fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { + // Commit the 'old' genesis block with Homestead transition at #2. + // Advance to block #4, past the homestead transition block of customg. + genesis := oldcustomg.MustCommit(db) + bc, _ := NewBlockChain(db, oldcustomg.Config, ethash.NewFullFaker(), new(event.TypeMux), vm.Config{}) + bc.SetValidator(bproc{}) + bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0)) + bc.CurrentBlock() + // This should return a compatibility error. + return SetupGenesisBlock(db, &customg) + }, + wantHash: customghash, + wantConfig: customg.Config, + wantErr: ¶ms.ConfigCompatError{ + What: "Homestead fork block", + StoredConfig: big.NewInt(2), + NewConfig: big.NewInt(3), + RewindTo: 1, + }, + }, + } + + for _, test := range tests { + db, _ := ethdb.NewMemDatabase() + config, hash, err := test.fn(db) + // Check the return values. + if !reflect.DeepEqual(err, test.wantErr) { + spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true} + t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr)) + } + if !reflect.DeepEqual(config, test.wantConfig) { + t.Errorf("%s:\nreturned %v\nwant %v", test.name, config, test.wantConfig) + } + if hash != test.wantHash { + t.Errorf("%s: returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex()) + } else if err == nil { + // Check database content. + stored := GetBlock(db, test.wantHash, 0) + if stored.Hash() != test.wantHash { + t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash) + } + } + } +} diff --git a/core/headerchain.go b/core/headerchain.go index a3d622087..9bb7f1793 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -18,21 +18,19 @@ package core import ( crand "crypto/rand" + "errors" "fmt" "math" "math/big" mrand "math/rand" - "runtime" - "sync" - "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "github.com/hashicorp/golang-lru" ) @@ -62,18 +60,15 @@ type HeaderChain struct { procInterrupt func() bool - rand *mrand.Rand - getValidator getHeaderValidatorFn + rand *mrand.Rand + engine consensus.Engine } -// getHeaderValidatorFn returns a HeaderValidator interface -type getHeaderValidatorFn func() HeaderValidator - // NewHeaderChain creates a new HeaderChain structure. // getValidator should return the parent's validator // procInterrupt points to the parent's interrupt semaphore // wg points to the parent's shutdown wait group -func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { +func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) { headerCache, _ := lru.New(headerCacheLimit) tdCache, _ := lru.New(tdCacheLimit) numberCache, _ := lru.New(numberCacheLimit) @@ -92,17 +87,12 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValid numberCache: numberCache, procInterrupt: procInterrupt, rand: mrand.New(mrand.NewSource(seed.Int64())), - getValidator: getValidator, + engine: engine, } hc.genesisHeader = hc.GetHeaderByNumber(0) if hc.genesisHeader == nil { - genesisBlock, err := WriteDefaultGenesisBlock(chainDb) - if err != nil { - return nil, err - } - log.Warn("Wrote default Ethereum genesis block") - hc.genesisHeader = genesisBlock.Header() + return nil, ErrNoGenesis } hc.currentHeader = hc.genesisHeader @@ -147,7 +137,7 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er // Calculate the total difficulty of the header ptd := hc.GetTd(header.ParentHash, number-1) if ptd == nil { - return NonStatTy, ParentError(header.ParentHash) + return NonStatTy, consensus.ErrUnknownAncestor } localTd := hc.GetTd(hc.currentHeaderHash, hc.currentHeader.Number.Uint64()) externTd := new(big.Int).Add(header.Difficulty, ptd) @@ -219,7 +209,8 @@ type WhCallback func(*types.Header) error // should be done or not. The reason behind the optional check is because some // of the header retrieval mechanisms already need to verfy nonces, as well as // because nonces can be verified sparsely, not needing to check each. -func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, writeHeader WhCallback) (int, error) { + +func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) { // Do a sanity check that the provided chain is actually ordered and linked for i := 1; i < len(chain); i++ { if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() { @@ -231,95 +222,53 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, w chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4]) } } - // Collect some import statistics to report on - stats := struct{ processed, ignored int }{} - start := time.Now() - // Generate the list of headers that should be POW verified - verify := make([]bool, len(chain)) - for i := 0; i < len(verify)/checkFreq; i++ { + // Generate the list of seal verification requests, and start the parallel verifier + seals := make([]bool, len(chain)) + for i := 0; i < len(seals)/checkFreq; i++ { index := i*checkFreq + hc.rand.Intn(checkFreq) - if index >= len(verify) { - index = len(verify) - 1 + if index >= len(seals) { + index = len(seals) - 1 } - verify[index] = true + seals[index] = true } - verify[len(verify)-1] = true // Last should always be verified to avoid junk + seals[len(seals)-1] = true // Last should always be verified to avoid junk - // Create the header verification task queue and worker functions - tasks := make(chan int, len(chain)) - for i := 0; i < len(chain); i++ { - tasks <- i - } - close(tasks) - - errs, failed := make([]error, len(tasks)), int32(0) - process := func(worker int) { - for index := range tasks { - header, hash := chain[index], chain[index].Hash() + abort, results := hc.engine.VerifyHeaders(hc, chain, seals) + defer close(abort) - // Short circuit insertion if shutting down or processing failed - if hc.procInterrupt() { - return - } - if atomic.LoadInt32(&failed) > 0 { - return - } - // Short circuit if the header is bad or already known - if BadHashes[hash] { - errs[index] = BadHashError(hash) - atomic.AddInt32(&failed, 1) - return - } - if hc.HasHeader(hash) { - continue - } - // Verify that the header honors the chain parameters - checkPow := verify[index] - - var err error - if index == 0 { - err = hc.getValidator().ValidateHeader(header, hc.GetHeader(header.ParentHash, header.Number.Uint64()-1), checkPow) - } else { - err = hc.getValidator().ValidateHeader(header, chain[index-1], checkPow) - } - if err != nil { - errs[index] = err - atomic.AddInt32(&failed, 1) - return - } + // Iterate over the headers and ensure they all check out + for i, header := range chain { + // If the chain is terminating, stop processing blocks + if hc.procInterrupt() { + log.Debug("Premature abort during headers verification") + return 0, errors.New("aborted") } - } - // Start as many worker threads as goroutines allowed - pending := new(sync.WaitGroup) - for i := 0; i < runtime.GOMAXPROCS(0); i++ { - pending.Add(1) - go func(id int) { - defer pending.Done() - process(id) - }(i) - } - pending.Wait() - - // If anything failed, report - if failed > 0 { - for i, err := range errs { - if err != nil { - return i, err - } + // If the header is a banned one, straight out abort + if BadHashes[header.Hash()] { + return i, ErrBlacklistedHash + } + // Otherwise wait for headers checks and ensure they pass + if err := <-results; err != nil { + return i, err } } + + return 0, nil +} + +func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCallback, start time.Time) (int, error) { + // Collect some import statistics to report on + stats := struct{ processed, ignored int }{} // All headers passed verification, import them into the database for i, header := range chain { // Short circuit insertion if shutting down if hc.procInterrupt() { - log.Debug("Premature abort during headers processing") - break + log.Debug("Premature abort during headers import") + return i, errors.New("aborted") } - hash := header.Hash() - // If the header's already known, skip it, otherwise store - if hc.HasHeader(hash) { + if hc.GetHeader(header.Hash(), header.Number.Uint64()) != nil { stats.ignored++ continue } @@ -490,35 +439,14 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) { hc.genesisHeader = head } -// headerValidator is responsible for validating block headers -// -// headerValidator implements HeaderValidator. -type headerValidator struct { - config *params.ChainConfig - hc *HeaderChain // Canonical header chain - Pow pow.PoW // Proof of work used for validating -} +// Config retrieves the header chain's chain configuration. +func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config } -// NewBlockValidator returns a new block validator which is safe for re-use -func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { - return &headerValidator{ - config: config, - Pow: pow, - hc: chain, - } -} +// Engine retrieves the header chain's consensus engine. +func (hc *HeaderChain) Engine() consensus.Engine { return hc.engine } -// ValidateHeader validates the given header and, depending on the pow arg, -// checks the proof of work of the given header. Returns an error if the -// validation failed. -func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error { - // Short circuit if the parent is missing. - if parent == nil { - return ParentError(header.ParentHash) - } - // Short circuit if the header's already known or its parent missing - if v.hc.HasHeader(header.Hash()) { - return nil - } - return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) +// GetBlock implements consensus.ChainReader, and returns nil for every input as +// a header chain does not have blocks available for retrieval. +func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block { + return nil } diff --git a/core/mkalloc.go b/core/mkalloc.go new file mode 100644 index 000000000..565e8c582 --- /dev/null +++ b/core/mkalloc.go @@ -0,0 +1,85 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// +build none + +/* + + The mkalloc tool creates the genesis allocation constants in genesis_alloc.go + It outputs a const declaration that contains an RLP-encoded list of (address, balance) tuples. + + go run mkalloc.go genesis.json + +*/ +package main + +import ( + "encoding/json" + "fmt" + "math/big" + "os" + "sort" + "strconv" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/rlp" +) + +type allocItem struct{ Addr, Balance *big.Int } + +type allocList []allocItem + +func (a allocList) Len() int { return len(a) } +func (a allocList) Less(i, j int) bool { return a[i].Addr.Cmp(a[j].Addr) < 0 } +func (a allocList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +func makelist(g *core.Genesis) allocList { + a := make(allocList, 0, len(g.Alloc)) + for addr, account := range g.Alloc { + if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 { + panic(fmt.Sprintf("can't encode account %x", addr)) + } + a = append(a, allocItem{addr.Big(), account.Balance}) + } + sort.Sort(a) + return a +} + +func makealloc(g *core.Genesis) string { + a := makelist(g) + data, err := rlp.EncodeToBytes(a) + if err != nil { + panic(err) + } + return strconv.QuoteToASCII(string(data)) +} + +func main() { + if len(os.Args) != 2 { + fmt.Fprintln(os.Stderr, "Usage: mkalloc genesis.json") + os.Exit(1) + } + + g := new(core.Genesis) + file, err := os.Open(os.Args[1]) + if err != nil { + panic(err) + } + if err := json.NewDecoder(file).Decode(g); err != nil { + panic(err) + } + fmt.Println("const allocData =", makealloc(g)) +} diff --git a/core/state/journal.go b/core/state/journal.go index 5cd41477d..73218dd28 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -73,6 +73,7 @@ type ( touchChange struct { account *common.Address prev bool + prevDirty bool } ) @@ -97,8 +98,10 @@ var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") func (ch touchChange) undo(s *StateDB) { if !ch.prev && *ch.account != ripemd { - delete(s.stateObjects, *ch.account) - delete(s.stateObjectsDirty, *ch.account) + s.getStateObject(*ch.account).touched = ch.prev + if !ch.prevDirty { + delete(s.stateObjectsDirty, *ch.account) + } } } diff --git a/core/state/state_object.go b/core/state/state_object.go index 7f994ee6d..7d3315303 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -137,8 +137,9 @@ func (self *stateObject) markSuicided() { func (c *stateObject) touch() { c.db.journal = append(c.db.journal, touchChange{ - account: &c.address, - prev: c.touched, + account: &c.address, + prev: c.touched, + prevDirty: c.onDirty == nil, }) if c.onDirty != nil { c.onDirty(c.Address()) diff --git a/core/state/statedb.go b/core/state/statedb.go index cfd033bd8..0c72fc6b0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -611,7 +611,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) // the root hash stored in a block. func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) { batch = s.db.NewBatch() - root, _ = s.commit(batch, deleteEmptyObjects) + root, _ = s.CommitTo(batch, deleteEmptyObjects) log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads()) return root, batch @@ -623,7 +623,8 @@ func (s *StateDB) clearJournalAndRefund() { s.refund = new(big.Int) } -func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { +// CommitTo writes the state to the given database. +func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { defer s.clearJournalAndRefund() // Commit objects to the trie. diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 597de3be5..72b638f97 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -116,7 +116,6 @@ func TestIntermediateLeaks(t *testing.T) { } func TestSnapshotRandom(t *testing.T) { - t.Skip("@fjl fix me please") config := &quick.Config{MaxCount: 1000} err := quick.Check((*snapshotTest).run, config) if cerr, ok := err.(*quick.CheckError); ok { diff --git a/core/state_processor.go b/core/state_processor.go index 3edc042a3..4fc2f1eae 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -19,6 +19,9 @@ package core import ( "math/big" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -26,25 +29,22 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var ( - big8 = big.NewInt(8) - big32 = big.NewInt(32) -) - // StateProcessor is a basic Processor, which takes care of transitioning // state from one point to another. // // StateProcessor implements Processor. type StateProcessor struct { - config *params.ChainConfig - bc *BlockChain + config *params.ChainConfig // Chain configuration options + bc *BlockChain // Canonical block chain + engine consensus.Engine // Consensus engine used for block rewards } // NewStateProcessor initialises a new StateProcessor. -func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcessor { +func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor { return &StateProcessor{ config: config, bc: bc, + engine: engine, } } @@ -59,42 +59,41 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg var ( receipts types.Receipts totalUsedGas = big.NewInt(0) - err error header = block.Header() allLogs []*types.Log gp = new(GasPool).AddGas(block.GasLimit()) ) // Mutate the the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { - ApplyDAOHardFork(statedb) + misc.ApplyDAOHardFork(statedb) } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - //fmt.Println("tx:", i) statedb.StartRecord(tx.Hash(), block.Hash(), i) - receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg) + receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg) if err != nil { return nil, nil, nil, err } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } - AccumulateRewards(statedb, header, block.Uncles()) + // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) + p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts) - return receipts, allLogs, totalUsedGas, err + return receipts, allLogs, totalUsedGas, nil } // ApplyTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) { +func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) { msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) if err != nil { return nil, nil, err } // Create a new context to be used in the EVM environment - context := NewEVMContext(msg, header, bc) + context := NewEVMContext(msg, header, bc, author) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. vmenv := vm.NewEVM(context, statedb, config, cfg) @@ -122,23 +121,3 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s return receipt, gas, err } - -// AccumulateRewards credits the coinbase of the given block with the -// mining 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(statedb *state.StateDB, header *types.Header, uncles []*types.Header) { - reward := new(big.Int).Set(BlockReward) - r := new(big.Int) - for _, uncle := range uncles { - r.Add(uncle.Number, big8) - r.Sub(r, header.Number) - r.Mul(r, BlockReward) - r.Div(r, big8) - statedb.AddBalance(uncle.Coinbase, r) - - r.Div(BlockReward, big32) - reward.Add(reward, r) - } - statedb.AddBalance(header.Coinbase, reward) -} diff --git a/core/state_transition.go b/core/state_transition.go index fb7518647..9e11144c6 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -18,6 +18,7 @@ package core import ( "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -195,26 +196,17 @@ func (self *StateTransition) buyGas() error { return nil } -func (self *StateTransition) preCheck() (err error) { +func (self *StateTransition) preCheck() error { msg := self.msg sender := self.from() // Make sure this transaction's nonce is correct if msg.CheckNonce() { if n := self.state.GetNonce(sender.Address()); n != msg.Nonce() { - return NonceError(msg.Nonce(), n) + return fmt.Errorf("invalid nonce: have %d, expected %d", msg.Nonce(), n) } } - - // Pre-pay gas - if err = self.buyGas(); err != nil { - if IsGasLimitErr(err) { - return err - } - return InvalidTxError(err) - } - - return nil + return self.buyGas() } // TransitionDb will transition the state by applying the current message and returning the result @@ -233,11 +225,10 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b // TODO convert to uint64 intrinsicGas := IntrinsicGas(self.data, contractCreation, homestead) if intrinsicGas.BitLen() > 64 { - return nil, nil, nil, InvalidTxError(vm.ErrOutOfGas) + return nil, nil, nil, vm.ErrOutOfGas } - if err = self.useGas(intrinsicGas.Uint64()); err != nil { - return nil, nil, nil, InvalidTxError(err) + return nil, nil, nil, err } var ( @@ -260,10 +251,9 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b // sufficient balance to make the transfer happen. The first // balance transfer may never fail. if vmerr == vm.ErrInsufficientBalance { - return nil, nil, nil, InvalidTxError(vmerr) + return nil, nil, nil, vmerr } } - requiredGas = new(big.Int).Set(self.gasUsed()) self.refundGas() diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 3a37dd2b9..765577933 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -42,7 +42,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { statedb, _ := state.New(common.Hash{}, db) key, _ := crypto.GenerateKey() - newPool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + newPool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) newPool.resetState() return newPool, key @@ -91,7 +91,7 @@ func TestStateChangeDuringPoolReset(t *testing.T) { gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) } - txpool := NewTxPool(testChainConfig(), mux, stateFunc, gasLimitFunc) + txpool := NewTxPool(params.TestChainConfig, mux, stateFunc, gasLimitFunc) txpool.resetState() nonce := txpool.State().GetNonce(address) @@ -564,7 +564,7 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) { db, _ := ethdb.NewMemDatabase() statedb, _ := state.New(common.Hash{}, db) - pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool.resetState() // Create a number of test accounts and fund them @@ -713,7 +713,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { db, _ := ethdb.NewMemDatabase() statedb, _ := state.New(common.Hash{}, db) - pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool.resetState() // Create a number of test accounts and fund them @@ -759,7 +759,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { db, _ := ethdb.NewMemDatabase() statedb, _ := state.New(common.Hash{}, db) - pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + pool := NewTxPool(params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) pool.resetState() // Create a number of test accounts and fund them diff --git a/core/types.go b/core/types.go index 7fd658979..1cfbbab29 100644 --- a/core/types.go +++ b/core/types.go @@ -24,31 +24,17 @@ import ( "github.com/ethereum/go-ethereum/core/vm" ) -// Validator is an interface which defines the standard for block validation. +// Validator is an interface which defines the standard for block validation. It +// is only responsible for validating block contents, as the header validation is +// done by the specific consensus engines. // -// The validator is responsible for validating incoming block or, if desired, -// validates headers for fast validation. -// -// ValidateBlock validates the given block and should return an error if it -// failed to do so and should be used for "full" validation. -// -// ValidateHeader validates the given header and parent and returns an error -// if it failed to do so. -// -// ValidateState validates the given statedb and optionally the receipts and -// gas used. The implementer should decide what to do with the given input. type Validator interface { - HeaderValidator - ValidateBlock(block *types.Block) error - ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error -} + // ValidateBody validates the given block's content. + ValidateBody(block *types.Block) error -// HeaderValidator is an interface for validating headers only -// -// ValidateHeader validates the given header and parent and returns an error -// if it failed to do so. -type HeaderValidator interface { - ValidateHeader(header, parent *types.Header, checkPow bool) error + // ValidateState validates the given statedb and optionally the receipts and + // gas used. + ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error } // Processor is an interface for processing blocks using a given initial state. diff --git a/core/types/block.go b/core/types/block.go index 1dae87962..8ca3d0e89 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -19,8 +19,6 @@ package types import ( "encoding/binary" - "encoding/json" - "errors" "fmt" "io" "math/big" @@ -39,12 +37,6 @@ var ( EmptyUncleHash = CalcUncleHash(nil) ) -var ( - errMissingHeaderMixDigest = errors.New("missing mixHash in JSON block header") - errMissingHeaderFields = errors.New("missing required JSON block header fields") - errBadNonceSize = errors.New("invalid block nonce size, want 8 bytes") -) - // A BlockNonce is a 64-bit hash which proves (combined with the // mix-hash) that a sufficient amount of computation has been carried // out on a block. @@ -72,41 +64,36 @@ func (n *BlockNonce) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) } +//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go + // Header represents a block header in the Ethereum blockchain. type Header struct { - ParentHash common.Hash // Hash to the previous block - UncleHash common.Hash // Uncles of this block - Coinbase common.Address // The coin base address - Root common.Hash // Block Trie state - TxHash common.Hash // Tx sha - ReceiptHash common.Hash // Receipt sha - Bloom Bloom // Bloom - Difficulty *big.Int // Difficulty for the current block - Number *big.Int // The block number - GasLimit *big.Int // Gas limit - GasUsed *big.Int // Gas used - Time *big.Int // Creation time - Extra []byte // Extra data - MixDigest common.Hash // for quick difficulty verification - Nonce BlockNonce -} - -type jsonHeader struct { - ParentHash *common.Hash `json:"parentHash"` - UncleHash *common.Hash `json:"sha3Uncles"` - Coinbase *common.Address `json:"miner"` - Root *common.Hash `json:"stateRoot"` - TxHash *common.Hash `json:"transactionsRoot"` - ReceiptHash *common.Hash `json:"receiptsRoot"` - Bloom *Bloom `json:"logsBloom"` - Difficulty *hexutil.Big `json:"difficulty"` - Number *hexutil.Big `json:"number"` - GasLimit *hexutil.Big `json:"gasLimit"` - GasUsed *hexutil.Big `json:"gasUsed"` - Time *hexutil.Big `json:"timestamp"` - Extra *hexutil.Bytes `json:"extraData"` - MixDigest *common.Hash `json:"mixHash"` - Nonce *BlockNonce `json:"nonce"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit *big.Int `json:"gasLimit" gencodec:"required"` + GasUsed *big.Int `json:"gasUsed" gencodec:"required"` + Time *big.Int `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash" gencodec:"required"` + Nonce BlockNonce `json:"nonce" gencodec:"required"` +} + +// field type overrides for gencodec +type headerMarshaling struct { + Difficulty *hexutil.Big + Number *hexutil.Big + GasLimit *hexutil.Big + GasUsed *hexutil.Big + Time *hexutil.Big + Extra hexutil.Bytes + Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON } // Hash returns the block hash of the header, which is simply the keccak256 hash of its @@ -134,65 +121,6 @@ func (h *Header) HashNoNonce() common.Hash { }) } -// MarshalJSON encodes headers into the web3 RPC response block format. -func (h *Header) MarshalJSON() ([]byte, error) { - return json.Marshal(&jsonHeader{ - ParentHash: &h.ParentHash, - UncleHash: &h.UncleHash, - Coinbase: &h.Coinbase, - Root: &h.Root, - TxHash: &h.TxHash, - ReceiptHash: &h.ReceiptHash, - Bloom: &h.Bloom, - Difficulty: (*hexutil.Big)(h.Difficulty), - Number: (*hexutil.Big)(h.Number), - GasLimit: (*hexutil.Big)(h.GasLimit), - GasUsed: (*hexutil.Big)(h.GasUsed), - Time: (*hexutil.Big)(h.Time), - Extra: (*hexutil.Bytes)(&h.Extra), - MixDigest: &h.MixDigest, - Nonce: &h.Nonce, - }) -} - -// UnmarshalJSON decodes headers from the web3 RPC response block format. -func (h *Header) UnmarshalJSON(input []byte) error { - var dec jsonHeader - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - // Ensure that all fields are set. MixDigest is checked separately because - // it is a recent addition to the spec (as of August 2016) and older RPC server - // implementations might not provide it. - if dec.MixDigest == nil { - return errMissingHeaderMixDigest - } - if dec.ParentHash == nil || dec.UncleHash == nil || dec.Coinbase == nil || - dec.Root == nil || dec.TxHash == nil || dec.ReceiptHash == nil || - dec.Bloom == nil || dec.Difficulty == nil || dec.Number == nil || - dec.GasLimit == nil || dec.GasUsed == nil || dec.Time == nil || - dec.Extra == nil || dec.Nonce == nil { - return errMissingHeaderFields - } - // Assign all values. - h.ParentHash = *dec.ParentHash - h.UncleHash = *dec.UncleHash - h.Coinbase = *dec.Coinbase - h.Root = *dec.Root - h.TxHash = *dec.TxHash - h.ReceiptHash = *dec.ReceiptHash - h.Bloom = *dec.Bloom - h.Difficulty = (*big.Int)(dec.Difficulty) - h.Number = (*big.Int)(dec.Number) - h.GasLimit = (*big.Int)(dec.GasLimit) - h.GasUsed = (*big.Int)(dec.GasUsed) - h.Time = (*big.Int)(dec.Time) - h.Extra = *dec.Extra - h.MixDigest = *dec.MixDigest - h.Nonce = *dec.Nonce - return nil -} - func rlpHash(x interface{}) (h common.Hash) { hw := sha3.NewKeccak256() rlp.Encode(hw, x) @@ -421,12 +349,11 @@ func CalcUncleHash(uncles []*Header) common.Hash { return rlpHash(uncles) } -// WithMiningResult returns a new block with the data from b -// where nonce and mix digest are set to the provided values. -func (b *Block) WithMiningResult(nonce BlockNonce, mixDigest common.Hash) *Block { - cpy := *b.header - cpy.Nonce = nonce - cpy.MixDigest = mixDigest +// WithSeal returns a new block with the data from b but the header replaced with +// the sealed one. +func (b *Block) WithSeal(header *Header) *Block { + cpy := *header + return &Block{ header: &cpy, transactions: b.transactions, diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go new file mode 100644 index 000000000..bcff7a940 --- /dev/null +++ b/core/types/gen_header_json.go @@ -0,0 +1,136 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +func (h Header) MarshalJSON() ([]byte, error) { + type Header struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Big `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` + Time *hexutil.Big `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash" gencodec:"required"` + Nonce BlockNonce `json:"nonce" gencodec:"required"` + Hash common.Hash `json:"hash"` + } + var enc Header + enc.ParentHash = h.ParentHash + enc.UncleHash = h.UncleHash + enc.Coinbase = h.Coinbase + enc.Root = h.Root + enc.TxHash = h.TxHash + enc.ReceiptHash = h.ReceiptHash + enc.Bloom = h.Bloom + enc.Difficulty = (*hexutil.Big)(h.Difficulty) + enc.Number = (*hexutil.Big)(h.Number) + enc.GasLimit = (*hexutil.Big)(h.GasLimit) + enc.GasUsed = (*hexutil.Big)(h.GasUsed) + enc.Time = (*hexutil.Big)(h.Time) + enc.Extra = h.Extra + enc.MixDigest = h.MixDigest + enc.Nonce = h.Nonce + enc.Hash = h.Hash() + return json.Marshal(&enc) +} + +func (h *Header) UnmarshalJSON(input []byte) error { + type Header struct { + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner" gencodec:"required"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Big `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` + Time *hexutil.Big `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest *common.Hash `json:"mixHash" gencodec:"required"` + Nonce *BlockNonce `json:"nonce" gencodec:"required"` + } + var dec Header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for Header") + } + h.ParentHash = *dec.ParentHash + if dec.UncleHash == nil { + return errors.New("missing required field 'sha3Uncles' for Header") + } + h.UncleHash = *dec.UncleHash + if dec.Coinbase == nil { + return errors.New("missing required field 'miner' for Header") + } + h.Coinbase = *dec.Coinbase + if dec.Root == nil { + return errors.New("missing required field 'stateRoot' for Header") + } + h.Root = *dec.Root + if dec.TxHash == nil { + return errors.New("missing required field 'transactionsRoot' for Header") + } + h.TxHash = *dec.TxHash + if dec.ReceiptHash == nil { + return errors.New("missing required field 'receiptsRoot' for Header") + } + h.ReceiptHash = *dec.ReceiptHash + if dec.Bloom == nil { + return errors.New("missing required field 'logsBloom' for Header") + } + h.Bloom = *dec.Bloom + if dec.Difficulty == nil { + return errors.New("missing required field 'difficulty' for Header") + } + h.Difficulty = (*big.Int)(dec.Difficulty) + if dec.Number == nil { + return errors.New("missing required field 'number' for Header") + } + h.Number = (*big.Int)(dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for Header") + } + h.GasLimit = (*big.Int)(dec.GasLimit) + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for Header") + } + h.GasUsed = (*big.Int)(dec.GasUsed) + if dec.Time == nil { + return errors.New("missing required field 'timestamp' for Header") + } + h.Time = (*big.Int)(dec.Time) + if dec.Extra == nil { + return errors.New("missing required field 'extraData' for Header") + } + h.Extra = dec.Extra + if dec.MixDigest == nil { + return errors.New("missing required field 'mixHash' for Header") + } + h.MixDigest = *dec.MixDigest + if dec.Nonce == nil { + return errors.New("missing required field 'nonce' for Header") + } + h.Nonce = *dec.Nonce + return nil +} diff --git a/core/types/gen_log_json.go b/core/types/gen_log_json.go new file mode 100644 index 000000000..92c699c2a --- /dev/null +++ b/core/types/gen_log_json.go @@ -0,0 +1,88 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +func (l Log) MarshalJSON() ([]byte, error) { + type Log struct { + Address common.Address `json:"address" gencodec:"required"` + Topics []common.Hash `json:"topics" gencodec:"required"` + Data hexutil.Bytes `json:"data" gencodec:"required"` + BlockNumber hexutil.Uint64 `json:"blockNumber"` + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + TxIndex hexutil.Uint `json:"transactionIndex" gencodec:"required"` + BlockHash common.Hash `json:"blockHash"` + Index hexutil.Uint `json:"logIndex" gencodec:"required"` + Removed bool `json:"removed"` + } + var enc Log + enc.Address = l.Address + enc.Topics = l.Topics + enc.Data = l.Data + enc.BlockNumber = hexutil.Uint64(l.BlockNumber) + enc.TxHash = l.TxHash + enc.TxIndex = hexutil.Uint(l.TxIndex) + enc.BlockHash = l.BlockHash + enc.Index = hexutil.Uint(l.Index) + enc.Removed = l.Removed + return json.Marshal(&enc) +} + +func (l *Log) UnmarshalJSON(input []byte) error { + type Log struct { + Address *common.Address `json:"address" gencodec:"required"` + Topics []common.Hash `json:"topics" gencodec:"required"` + Data hexutil.Bytes `json:"data" gencodec:"required"` + BlockNumber *hexutil.Uint64 `json:"blockNumber"` + TxHash *common.Hash `json:"transactionHash" gencodec:"required"` + TxIndex *hexutil.Uint `json:"transactionIndex" gencodec:"required"` + BlockHash *common.Hash `json:"blockHash"` + Index *hexutil.Uint `json:"logIndex" gencodec:"required"` + Removed *bool `json:"removed"` + } + var dec Log + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Address == nil { + return errors.New("missing required field 'address' for Log") + } + l.Address = *dec.Address + if dec.Topics == nil { + return errors.New("missing required field 'topics' for Log") + } + l.Topics = dec.Topics + if dec.Data == nil { + return errors.New("missing required field 'data' for Log") + } + l.Data = dec.Data + if dec.BlockNumber != nil { + l.BlockNumber = uint64(*dec.BlockNumber) + } + if dec.TxHash == nil { + return errors.New("missing required field 'transactionHash' for Log") + } + l.TxHash = *dec.TxHash + if dec.TxIndex == nil { + return errors.New("missing required field 'transactionIndex' for Log") + } + l.TxIndex = uint(*dec.TxIndex) + if dec.BlockHash != nil { + l.BlockHash = *dec.BlockHash + } + if dec.Index == nil { + return errors.New("missing required field 'logIndex' for Log") + } + l.Index = uint(*dec.Index) + if dec.Removed != nil { + l.Removed = *dec.Removed + } + return nil +} diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go new file mode 100644 index 000000000..edbd64ba4 --- /dev/null +++ b/core/types/gen_receipt_json.go @@ -0,0 +1,77 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +func (r Receipt) MarshalJSON() ([]byte, error) { + type Receipt struct { + PostState hexutil.Bytes `json:"root" gencodec:"required"` + CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Logs []*Log `json:"logs" gencodec:"required"` + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + ContractAddress common.Address `json:"contractAddress"` + GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` + } + var enc Receipt + enc.PostState = r.PostState + enc.CumulativeGasUsed = (*hexutil.Big)(r.CumulativeGasUsed) + enc.Bloom = r.Bloom + enc.Logs = r.Logs + enc.TxHash = r.TxHash + enc.ContractAddress = r.ContractAddress + enc.GasUsed = (*hexutil.Big)(r.GasUsed) + return json.Marshal(&enc) +} + +func (r *Receipt) UnmarshalJSON(input []byte) error { + type Receipt struct { + PostState hexutil.Bytes `json:"root" gencodec:"required"` + CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Logs []*Log `json:"logs" gencodec:"required"` + TxHash *common.Hash `json:"transactionHash" gencodec:"required"` + ContractAddress *common.Address `json:"contractAddress"` + GasUsed *hexutil.Big `json:"gasUsed" gencodec:"required"` + } + var dec Receipt + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.PostState == nil { + return errors.New("missing required field 'root' for Receipt") + } + r.PostState = dec.PostState + if dec.CumulativeGasUsed == nil { + return errors.New("missing required field 'cumulativeGasUsed' for Receipt") + } + r.CumulativeGasUsed = (*big.Int)(dec.CumulativeGasUsed) + if dec.Bloom == nil { + return errors.New("missing required field 'logsBloom' for Receipt") + } + r.Bloom = *dec.Bloom + if dec.Logs == nil { + return errors.New("missing required field 'logs' for Receipt") + } + r.Logs = dec.Logs + if dec.TxHash == nil { + return errors.New("missing required field 'transactionHash' for Receipt") + } + r.TxHash = *dec.TxHash + if dec.ContractAddress != nil { + r.ContractAddress = *dec.ContractAddress + } + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for Receipt") + } + r.GasUsed = (*big.Int)(dec.GasUsed) + return nil +} diff --git a/core/types/gen_tx_json.go b/core/types/gen_tx_json.go new file mode 100644 index 000000000..4fb658e0d --- /dev/null +++ b/core/types/gen_tx_json.go @@ -0,0 +1,97 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +func (t txdata) MarshalJSON() ([]byte, error) { + type txdata struct { + AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"` + Price *hexutil.Big `json:"gasPrice" gencodec:"required"` + GasLimit *hexutil.Big `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` + Amount *hexutil.Big `json:"value" gencodec:"required"` + Payload hexutil.Bytes `json:"input" gencodec:"required"` + V *hexutil.Big `json:"v" gencodec:"required"` + R *hexutil.Big `json:"r" gencodec:"required"` + S *hexutil.Big `json:"s" gencodec:"required"` + Hash *common.Hash `json:"hash" rlp:"-"` + } + var enc txdata + enc.AccountNonce = hexutil.Uint64(t.AccountNonce) + enc.Price = (*hexutil.Big)(t.Price) + enc.GasLimit = (*hexutil.Big)(t.GasLimit) + enc.Recipient = t.Recipient + enc.Amount = (*hexutil.Big)(t.Amount) + enc.Payload = t.Payload + enc.V = (*hexutil.Big)(t.V) + enc.R = (*hexutil.Big)(t.R) + enc.S = (*hexutil.Big)(t.S) + enc.Hash = t.Hash + return json.Marshal(&enc) +} + +func (t *txdata) UnmarshalJSON(input []byte) error { + type txdata struct { + AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"` + Price *hexutil.Big `json:"gasPrice" gencodec:"required"` + GasLimit *hexutil.Big `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` + Amount *hexutil.Big `json:"value" gencodec:"required"` + Payload hexutil.Bytes `json:"input" gencodec:"required"` + V *hexutil.Big `json:"v" gencodec:"required"` + R *hexutil.Big `json:"r" gencodec:"required"` + S *hexutil.Big `json:"s" gencodec:"required"` + Hash *common.Hash `json:"hash" rlp:"-"` + } + var dec txdata + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.AccountNonce == nil { + return errors.New("missing required field 'nonce' for txdata") + } + t.AccountNonce = uint64(*dec.AccountNonce) + if dec.Price == nil { + return errors.New("missing required field 'gasPrice' for txdata") + } + t.Price = (*big.Int)(dec.Price) + if dec.GasLimit == nil { + return errors.New("missing required field 'gas' for txdata") + } + t.GasLimit = (*big.Int)(dec.GasLimit) + if dec.Recipient != nil { + t.Recipient = dec.Recipient + } + if dec.Amount == nil { + return errors.New("missing required field 'value' for txdata") + } + t.Amount = (*big.Int)(dec.Amount) + if dec.Payload == nil { + return errors.New("missing required field 'input' for txdata") + } + t.Payload = dec.Payload + if dec.V == nil { + return errors.New("missing required field 'v' for txdata") + } + t.V = (*big.Int)(dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' for txdata") + } + t.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' for txdata") + } + t.S = (*big.Int)(dec.S) + if dec.Hash != nil { + t.Hash = dec.Hash + } + return nil +} diff --git a/core/types/log.go b/core/types/log.go index 7efb06b5c..be5de38da 100644 --- a/core/types/log.go +++ b/core/types/log.go @@ -17,8 +17,6 @@ package types import ( - "encoding/json" - "errors" "fmt" "io" @@ -27,27 +25,42 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -var errMissingLogFields = errors.New("missing required JSON log fields") +//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go // Log represents a contract log event. These events are generated by the LOG opcode and // stored/indexed by the node. type Log struct { - // Consensus fields. - Address common.Address // address of the contract that generated the event - Topics []common.Hash // list of topics provided by the contract. - Data []byte // supplied by the contract, usually ABI-encoded + // Consensus fields: + // address of the contract that generated the event + Address common.Address `json:"address" gencodec:"required"` + // list of topics provided by the contract. + Topics []common.Hash `json:"topics" gencodec:"required"` + // supplied by the contract, usually ABI-encoded + Data []byte `json:"data" gencodec:"required"` // Derived fields. These fields are filled in by the node // but not secured by consensus. - BlockNumber uint64 // block in which the transaction was included - TxHash common.Hash // hash of the transaction - TxIndex uint // index of the transaction in the block - BlockHash common.Hash // hash of the block in which the transaction was included - Index uint // index of the log in the receipt + // block in which the transaction was included + BlockNumber uint64 `json:"blockNumber"` + // hash of the transaction + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + // index of the transaction in the block + TxIndex uint `json:"transactionIndex" gencodec:"required"` + // hash of the block in which the transaction was included + BlockHash common.Hash `json:"blockHash"` + // index of the log in the receipt + Index uint `json:"logIndex" gencodec:"required"` // The Removed field is true if this log was reverted due to a chain reorganisation. // You must pay attention to this field if you receive logs through a filter query. - Removed bool + Removed bool `json:"removed"` +} + +type logMarshaling struct { + Data hexutil.Bytes + BlockNumber hexutil.Uint64 + TxIndex hexutil.Uint + Index hexutil.Uint } type rlpLog struct { @@ -67,18 +80,6 @@ type rlpStorageLog struct { Index uint } -type jsonLog struct { - Address *common.Address `json:"address"` - Topics *[]common.Hash `json:"topics"` - Data *hexutil.Bytes `json:"data"` - BlockNumber *hexutil.Uint64 `json:"blockNumber"` - TxIndex *hexutil.Uint `json:"transactionIndex"` - TxHash *common.Hash `json:"transactionHash"` - BlockHash *common.Hash `json:"blockHash"` - Index *hexutil.Uint `json:"logIndex"` - Removed bool `json:"removed"` -} - // EncodeRLP implements rlp.Encoder. func (l *Log) EncodeRLP(w io.Writer) error { return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}) @@ -98,54 +99,6 @@ func (l *Log) String() string { return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index) } -// MarshalJSON implements json.Marshaler. -func (l *Log) MarshalJSON() ([]byte, error) { - jslog := &jsonLog{ - Address: &l.Address, - Topics: &l.Topics, - Data: (*hexutil.Bytes)(&l.Data), - TxIndex: (*hexutil.Uint)(&l.TxIndex), - TxHash: &l.TxHash, - Index: (*hexutil.Uint)(&l.Index), - Removed: l.Removed, - } - // Set block information for mined logs. - if (l.BlockHash != common.Hash{}) { - jslog.BlockHash = &l.BlockHash - jslog.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber) - } - return json.Marshal(jslog) -} - -// UnmarshalJSON implements json.Umarshaler. -func (l *Log) UnmarshalJSON(input []byte) error { - var dec jsonLog - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.Address == nil || dec.Topics == nil || dec.Data == nil || - dec.TxIndex == nil || dec.TxHash == nil || dec.Index == nil { - return errMissingLogFields - } - declog := Log{ - Address: *dec.Address, - Topics: *dec.Topics, - Data: *dec.Data, - TxHash: *dec.TxHash, - TxIndex: uint(*dec.TxIndex), - Index: uint(*dec.Index), - Removed: dec.Removed, - } - // Block information may be missing if the log is received through - // the pending log filter, so it's handled specially here. - if dec.BlockHash != nil && dec.BlockNumber != nil { - declog.BlockHash = *dec.BlockHash - declog.BlockNumber = uint64(*dec.BlockNumber) - } - *l = declog - return nil -} - // LogForStorage is a wrapper around a Log that flattens and parses the entire content of // a log including non-consensus fields. type LogForStorage Log diff --git a/core/types/log_test.go b/core/types/log_test.go index bf742ccac..0e56acfe4 100644 --- a/core/types/log_test.go +++ b/core/types/log_test.go @@ -18,6 +18,7 @@ package types import ( "encoding/json" + "fmt" "reflect" "testing" @@ -96,7 +97,7 @@ var unmarshalLogTests = map[string]struct { }, "missing data": { input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, - wantError: errMissingLogFields, + wantError: fmt.Errorf("missing required field 'data' for Log"), }, } diff --git a/core/types/receipt.go b/core/types/receipt.go index 0a6a35e33..ef6f6a2bb 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -17,8 +17,6 @@ package types import ( - "encoding/json" - "errors" "fmt" "io" "math/big" @@ -28,33 +26,26 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -var ( - errMissingReceiptPostState = errors.New("missing post state root in JSON receipt") - errMissingReceiptFields = errors.New("missing required JSON receipt fields") -) +//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go // Receipt represents the results of a transaction. type Receipt struct { // Consensus fields - PostState []byte - CumulativeGasUsed *big.Int - Bloom Bloom - Logs []*Log + PostState []byte `json:"root" gencodec:"required"` + CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Logs []*Log `json:"logs" gencodec:"required"` // Implementation fields (don't reorder!) - TxHash common.Hash - ContractAddress common.Address - GasUsed *big.Int + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + ContractAddress common.Address `json:"contractAddress"` + GasUsed *big.Int `json:"gasUsed" gencodec:"required"` } -type jsonReceipt struct { - PostState *common.Hash `json:"root"` - CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"` - Bloom *Bloom `json:"logsBloom"` - Logs []*Log `json:"logs"` - TxHash *common.Hash `json:"transactionHash"` - ContractAddress *common.Address `json:"contractAddress"` - GasUsed *hexutil.Big `json:"gasUsed"` +type receiptMarshaling struct { + PostState hexutil.Bytes + CumulativeGasUsed *hexutil.Big + GasUsed *hexutil.Big } // NewReceipt creates a barebone transaction receipt, copying the init fields. @@ -84,51 +75,6 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { return nil } -// MarshalJSON encodes receipts into the web3 RPC response block format. -func (r *Receipt) MarshalJSON() ([]byte, error) { - root := common.BytesToHash(r.PostState) - - return json.Marshal(&jsonReceipt{ - PostState: &root, - CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed), - Bloom: &r.Bloom, - Logs: r.Logs, - TxHash: &r.TxHash, - ContractAddress: &r.ContractAddress, - GasUsed: (*hexutil.Big)(r.GasUsed), - }) -} - -// UnmarshalJSON decodes the web3 RPC receipt format. -func (r *Receipt) UnmarshalJSON(input []byte) error { - var dec jsonReceipt - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - // Ensure that all fields are set. PostState is checked separately because it is a - // recent addition to the RPC spec (as of August 2016) and older implementations might - // not provide it. Note that ContractAddress is not checked because it can be null. - if dec.PostState == nil { - return errMissingReceiptPostState - } - if dec.CumulativeGasUsed == nil || dec.Bloom == nil || - dec.Logs == nil || dec.TxHash == nil || dec.GasUsed == nil { - return errMissingReceiptFields - } - *r = Receipt{ - PostState: (*dec.PostState)[:], - CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed), - Bloom: *dec.Bloom, - Logs: dec.Logs, - TxHash: *dec.TxHash, - GasUsed: (*big.Int)(dec.GasUsed), - } - if dec.ContractAddress != nil { - r.ContractAddress = *dec.ContractAddress - } - return nil -} - // String implements the Stringer interface. func (r *Receipt) String() string { return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs) diff --git a/core/types/transaction.go b/core/types/transaction.go index ab0bba4dc..8e108b2a3 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -18,7 +18,6 @@ package types import ( "container/heap" - "encoding/json" "errors" "fmt" "io" @@ -28,22 +27,20 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) -var ErrInvalidSig = errors.New("invalid transaction v, r, s values") +//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go var ( - errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields") - errMissingTxFields = errors.New("missing required JSON transaction fields") - errNoSigner = errors.New("missing signing methods") + ErrInvalidSig = errors.New("invalid transaction v, r, s values") + errNoSigner = errors.New("missing signing methods") ) // deriveSigner makes a *best* guess about which signer to use. func deriveSigner(V *big.Int) Signer { if V.Sign() != 0 && isProtectedV(V) { - return EIP155Signer{chainId: deriveChainId(V)} + return NewEIP155Signer(deriveChainId(V)) } else { return HomesteadSigner{} } @@ -58,26 +55,31 @@ type Transaction struct { } type txdata struct { - AccountNonce uint64 - Price, GasLimit *big.Int - Recipient *common.Address `rlp:"nil"` // nil means contract creation - Amount *big.Int - Payload []byte - V *big.Int // signature - R, S *big.Int // signature -} - -type jsonTransaction struct { - Hash *common.Hash `json:"hash"` - AccountNonce *hexutil.Uint64 `json:"nonce"` - Price *hexutil.Big `json:"gasPrice"` - GasLimit *hexutil.Big `json:"gas"` - Recipient *common.Address `json:"to"` - Amount *hexutil.Big `json:"value"` - Payload *hexutil.Bytes `json:"input"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` + AccountNonce uint64 `json:"nonce" gencodec:"required"` + Price *big.Int `json:"gasPrice" gencodec:"required"` + GasLimit *big.Int `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation + Amount *big.Int `json:"value" gencodec:"required"` + Payload []byte `json:"input" gencodec:"required"` + + // Signature values + V *big.Int `json:"v" gencodec:"required"` + R *big.Int `json:"r" gencodec:"required"` + S *big.Int `json:"s" gencodec:"required"` + + // This is only used when marshaling to JSON. + Hash *common.Hash `json:"hash" rlp:"-"` +} + +type txdataMarshaling struct { + AccountNonce hexutil.Uint64 + Price *hexutil.Big + GasLimit *hexutil.Big + Amount *hexutil.Big + Payload hexutil.Bytes + V *hexutil.Big + R *hexutil.Big + S *hexutil.Big } func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { @@ -116,19 +118,6 @@ func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice return &Transaction{data: d} } -func pickSigner(rules params.Rules) Signer { - var signer Signer - switch { - case rules.IsEIP155: - signer = NewEIP155Signer(rules.ChainId) - case rules.IsHomestead: - signer = HomesteadSigner{} - default: - signer = FrontierSigner{} - } - return signer -} - // ChainId returns which chain id this transaction was signed for (if at all) func (tx *Transaction) ChainId() *big.Int { return deriveChainId(tx.data.V) @@ -164,66 +153,30 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { return err } -// MarshalJSON encodes transactions into the web3 RPC response block format. func (tx *Transaction) MarshalJSON() ([]byte, error) { hash := tx.Hash() - - return json.Marshal(&jsonTransaction{ - Hash: &hash, - AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce), - Price: (*hexutil.Big)(tx.data.Price), - GasLimit: (*hexutil.Big)(tx.data.GasLimit), - Recipient: tx.data.Recipient, - Amount: (*hexutil.Big)(tx.data.Amount), - Payload: (*hexutil.Bytes)(&tx.data.Payload), - V: (*hexutil.Big)(tx.data.V), - R: (*hexutil.Big)(tx.data.R), - S: (*hexutil.Big)(tx.data.S), - }) + data := tx.data + data.Hash = &hash + return data.MarshalJSON() } // UnmarshalJSON decodes the web3 RPC transaction format. func (tx *Transaction) UnmarshalJSON(input []byte) error { - var dec jsonTransaction - if err := json.Unmarshal(input, &dec); err != nil { + var dec txdata + if err := dec.UnmarshalJSON(input); err != nil { return err } - // Ensure that all fields are set. V, R, S are checked separately because they're a - // recent addition to the RPC spec (as of August 2016) and older implementations might - // not provide them. Note that Recipient is not checked because it can be missing for - // contract creations. - if dec.V == nil || dec.R == nil || dec.S == nil { - return errMissingTxSignatureFields - } - var V byte - if isProtectedV((*big.Int)(dec.V)) { - chainId := deriveChainId((*big.Int)(dec.V)).Uint64() - V = byte(dec.V.ToInt().Uint64() - 35 - 2*chainId) + if isProtectedV(dec.V) { + chainId := deriveChainId(dec.V).Uint64() + V = byte(dec.V.Uint64() - 35 - 2*chainId) } else { - V = byte(((*big.Int)(dec.V)).Uint64() - 27) + V = byte(dec.V.Uint64() - 27) } - if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) { + if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) { return ErrInvalidSig } - - if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil { - return errMissingTxFields - } - // Assign the fields. This is not atomic but reusing transactions - // for decoding isn't thread safe anyway. - *tx = Transaction{} - tx.data = txdata{ - AccountNonce: uint64(*dec.AccountNonce), - Recipient: dec.Recipient, - Amount: (*big.Int)(dec.Amount), - GasLimit: (*big.Int)(dec.GasLimit), - Price: (*big.Int)(dec.Price), - Payload: *dec.Payload, - V: (*big.Int)(dec.V), - R: (*big.Int)(dec.R), - S: (*big.Int)(dec.S), - } + *tx = Transaction{data: dec} return nil } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index e7eb7c5f0..b4bab0aad 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -112,6 +112,9 @@ type EIP155Signer struct { } func NewEIP155Signer(chainId *big.Int) EIP155Signer { + if chainId == nil { + chainId = new(big.Int) + } return EIP155Signer{ chainId: chainId, chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)), diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7d12ebc05..8ee9d3ca7 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -18,7 +18,6 @@ package vm import ( "fmt" - "math/big" "sync/atomic" "time" @@ -117,9 +116,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e if err != nil && evm.cfg.Debug { // XXX For debugging //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d ERR = %v\n", pc, op, cost, stack.len(), err) - // TODO update the tracer - g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost) - evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err) } }() @@ -177,8 +174,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e } if evm.cfg.Debug { - g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost) - evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err) + evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err) } // XXX For debugging //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d\n", pc, op, cost, stack.len()) diff --git a/core/vm/logger.go b/core/vm/logger.go index 3d7e1c95f..825025b05 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -52,8 +52,8 @@ type LogConfig struct { type StructLog struct { Pc uint64 Op OpCode - Gas *big.Int - GasCost *big.Int + Gas uint64 + GasCost uint64 Memory []byte Stack []*big.Int Storage map[common.Hash]common.Hash @@ -67,7 +67,7 @@ type StructLog struct { // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. type Tracer interface { - CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error + CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error } // StructLogger is an EVM state logger and implements Tracer. @@ -96,7 +96,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger { // captureState logs a new structured log message and pushes it out to the environment // // captureState also tracks SSTORE ops to track dirty values. -func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { +func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { // check if already accumulated the specified number of logs if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { return ErrTraceLimitReached @@ -158,7 +158,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *b } } // create a new snaptshot of the EVM. - log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.depth, err} + log := StructLog{pc, op, gas, cost, mem, stck, storage, env.depth, err} l.logs = append(l.logs, log) return nil diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index e755a18e2..b6fa31132 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -59,7 +59,7 @@ func TestStoreCapture(t *testing.T) { var index common.Hash - logger.CaptureState(env, 0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) + logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) if len(logger.changedValues[contract.Address()]) == 0 { t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) } @@ -81,13 +81,13 @@ func TestStorageCapture(t *testing.T) { stack = newstack() ) - logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) + logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil) if ref.calledForEach { t.Error("didn't expect for each to be called") } logger = NewStructLogger(&LogConfig{FullStorage: true}) - logger.CaptureState(env, 0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) + logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil) if !ref.calledForEach { t.Error("expected for each to be called") } diff --git a/eth/api.go b/eth/api.go index b17968ebb..b386c08b4 100644 --- a/eth/api.go +++ b/eth/api.go @@ -19,13 +19,13 @@ package eth import ( "bytes" "compress/gzip" + "context" "errors" "fmt" "io" "io/ioutil" "math/big" "os" - "runtime" "strings" "time" @@ -36,10 +36,11 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/net/context" + "github.com/ethereum/go-ethereum/rpc" ) const defaultTraceTimeout = 5 * time.Second @@ -56,18 +57,18 @@ func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI { } // Etherbase is the address that mining rewards will be send to -func (s *PublicEthereumAPI) Etherbase() (common.Address, error) { - return s.e.Etherbase() +func (api *PublicEthereumAPI) Etherbase() (common.Address, error) { + return api.e.Etherbase() } // Coinbase is the address that mining rewards will be send to (alias for Etherbase) -func (s *PublicEthereumAPI) Coinbase() (common.Address, error) { - return s.Etherbase() +func (api *PublicEthereumAPI) Coinbase() (common.Address, error) { + return api.Etherbase() } // Hashrate returns the POW hashrate -func (s *PublicEthereumAPI) Hashrate() hexutil.Uint64 { - return hexutil.Uint64(s.e.Miner().HashRate()) +func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 { + return hexutil.Uint64(api.e.Miner().HashRate()) } // PublicMinerAPI provides an API to control the miner. @@ -79,34 +80,34 @@ type PublicMinerAPI struct { // NewPublicMinerAPI create a new PublicMinerAPI instance. func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI { - agent := miner.NewRemoteAgent(e.Pow()) + agent := miner.NewRemoteAgent(e.BlockChain(), e.Engine()) e.Miner().Register(agent) return &PublicMinerAPI{e, agent} } // Mining returns an indication if this node is currently mining. -func (s *PublicMinerAPI) Mining() bool { - return s.e.IsMining() +func (api *PublicMinerAPI) Mining() bool { + return api.e.IsMining() } // SubmitWork can be used by external miner to submit their POW solution. It returns an indication if the work was // accepted. Note, this is not an indication if the provided work was valid! -func (s *PublicMinerAPI) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool { - return s.agent.SubmitWork(nonce, digest, solution) +func (api *PublicMinerAPI) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool { + return api.agent.SubmitWork(nonce, digest, solution) } // GetWork returns a work package for external miner. The work package consists of 3 strings // result[0], 32 bytes hex encoded current block header pow-hash // result[1], 32 bytes hex encoded seed hash used for DAG // result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty -func (s *PublicMinerAPI) GetWork() ([3]string, error) { - if !s.e.IsMining() { - if err := s.e.StartMining(0); err != nil { +func (api *PublicMinerAPI) GetWork() ([3]string, error) { + if !api.e.IsMining() { + if err := api.e.StartMining(false); err != nil { return [3]string{}, err } } - work, err := s.agent.GetWork() + work, err := api.agent.GetWork() if err != nil { return work, fmt.Errorf("mining not ready: %v", err) } @@ -116,8 +117,8 @@ func (s *PublicMinerAPI) GetWork() ([3]string, error) { // SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined // hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which // must be unique between nodes. -func (s *PublicMinerAPI) SubmitHashrate(hashrate hexutil.Uint64, id common.Hash) bool { - s.agent.SubmitHashrate(id, uint64(hashrate)) +func (api *PublicMinerAPI) SubmitHashrate(hashrate hexutil.Uint64, id common.Hash) bool { + api.agent.SubmitHashrate(id, uint64(hashrate)) return true } @@ -132,47 +133,66 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI { return &PrivateMinerAPI{e: e} } -// Start the miner with the given number of threads. If threads is nil the number of -// workers started is equal to the number of logical CPU's that are usable by this process. -func (s *PrivateMinerAPI) Start(threads *int) (bool, error) { - var err error +// Start the miner with the given number of threads. If threads is nil the number +// of workers started is equal to the number of logical CPUs that are usable by +// this process. If mining is already running, this method adjust the number of +// threads allowed to use. +func (api *PrivateMinerAPI) Start(threads *int) error { + // Set the number of threads if the seal engine supports it if threads == nil { - err = s.e.StartMining(runtime.NumCPU()) - } else { - err = s.e.StartMining(*threads) + threads = new(int) + } else if *threads == 0 { + *threads = -1 // Disable the miner from within + } + type threaded interface { + SetThreads(threads int) + } + if th, ok := api.e.engine.(threaded); ok { + log.Info("Updated mining threads", "threads", *threads) + th.SetThreads(*threads) } - return err == nil, err + // Start the miner and return + if !api.e.IsMining() { + return api.e.StartMining(true) + } + return nil } // Stop the miner -func (s *PrivateMinerAPI) Stop() bool { - s.e.StopMining() +func (api *PrivateMinerAPI) Stop() bool { + type threaded interface { + SetThreads(threads int) + } + if th, ok := api.e.engine.(threaded); ok { + th.SetThreads(-1) + } + api.e.StopMining() return true } // SetExtra sets the extra data string that is included when this miner mines a block. -func (s *PrivateMinerAPI) SetExtra(extra string) (bool, error) { - if err := s.e.Miner().SetExtra([]byte(extra)); err != nil { +func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) { + if err := api.e.Miner().SetExtra([]byte(extra)); err != nil { return false, err } return true, nil } // SetGasPrice sets the minimum accepted gas price for the miner. -func (s *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { - s.e.Miner().SetGasPrice((*big.Int)(&gasPrice)) +func (api *PrivateMinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { + api.e.Miner().SetGasPrice((*big.Int)(&gasPrice)) return true } // SetEtherbase sets the etherbase of the miner -func (s *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool { - s.e.SetEtherbase(etherbase) +func (api *PrivateMinerAPI) SetEtherbase(etherbase common.Address) bool { + api.e.SetEtherbase(etherbase) return true } // GetHashrate returns the current hashrate of the miner. -func (s *PrivateMinerAPI) GetHashrate() uint64 { - return uint64(s.e.miner.HashRate()) +func (api *PrivateMinerAPI) GetHashrate() uint64 { + return uint64(api.e.miner.HashRate()) } // PrivateAdminAPI is the collection of Etheruem full node-related APIs @@ -281,10 +301,22 @@ func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI { } // DumpBlock retrieves the entire state of the database at a given block. -func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) { - block := api.eth.BlockChain().GetBlockByNumber(number) +func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) { + if blockNr == rpc.PendingBlockNumber { + // If we're dumping the pending state, we need to request + // both the pending block as well as the pending state from + // the miner and operate on those + _, stateDb := api.eth.miner.Pending() + return stateDb.RawDump(), nil + } + var block *types.Block + if blockNr == rpc.LatestBlockNumber { + block = api.eth.blockchain.CurrentBlock() + } else { + block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr)) + } if block == nil { - return state.Dump{}, fmt.Errorf("block #%d not found", number) + return state.Dump{}, fmt.Errorf("block #%d not found", blockNr) } stateDb, err := api.eth.BlockChain().StateAt(block.Root()) if err != nil { @@ -321,7 +353,7 @@ type TraceArgs struct { Timeout *string } -// TraceBlock processes the given block's RLP but does not import the block in to +// TraceBlock processes the given block'api RLP but does not import the block in to // the chain. func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) BlockTraceResult { var block types.Block @@ -338,7 +370,7 @@ func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) Bl } } -// TraceBlockFromFile loads the block's RLP from the given file name and attempts to +// TraceBlockFromFile loads the block'api RLP from the given file name and attempts to // process it but does not import the block in to the chain. func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig) BlockTraceResult { blockRlp, err := ioutil.ReadFile(file) @@ -349,11 +381,21 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig } // TraceBlockByNumber processes the block by canonical block number. -func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.LogConfig) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockByNumber(blockNr rpc.BlockNumber, config *vm.LogConfig) BlockTraceResult { // Fetch the block that we aim to reprocess - block := api.eth.BlockChain().GetBlockByNumber(number) + var block *types.Block + switch blockNr { + case rpc.PendingBlockNumber: + // Pending block is only known by the miner + block = api.eth.miner.PendingBlock() + case rpc.LatestBlockNumber: + block = api.eth.blockchain.CurrentBlock() + default: + block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr)) + } + if block == nil { - return BlockTraceResult{Error: fmt.Sprintf("block #%d not found", number)} + return BlockTraceResult{Error: fmt.Sprintf("block #%d not found", blockNr)} } validated, logs, err := api.traceBlock(block, config) @@ -395,8 +437,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConf Debug: true, Tracer: structLogger, } - - if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash(), block.NumberU64()-1), true, false); err != nil { + if err := api.eth.engine.VerifyHeader(blockchain, block.Header(), true); err != nil { return false, structLogger.StructLogs(), err } statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) @@ -507,7 +548,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. if err != nil { return nil, fmt.Errorf("sender retrieval failed: %v", err) } - context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain()) + context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain(), nil) // Mutate the state if we haven't reached the tracing transaction yet if uint64(idx) < txIndex { diff --git a/eth/api_backend.go b/eth/api_backend.go index 5a5c4c532..fe108d272 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -17,6 +17,7 @@ package eth import ( + "context" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -33,13 +34,12 @@ import ( "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) // EthApiBackend implements ethapi.Backend for full nodes type EthApiBackend struct { eth *Ethereum - gpo *gasprice.GasPriceOracle + gpo *gasprice.Oracle } func (b *EthApiBackend) ChainConfig() *params.ChainConfig { @@ -51,6 +51,7 @@ func (b *EthApiBackend) CurrentBlock() *types.Block { } func (b *EthApiBackend) SetHead(number uint64) { + b.eth.protocolManager.downloader.Cancel() b.eth.blockchain.SetHead(number) } @@ -113,7 +114,7 @@ func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state etha from.SetBalance(math.MaxBig256) vmError := func() error { return nil } - context := core.NewEVMContext(msg, header, b.eth.BlockChain()) + context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil) return vm.NewEVM(context, statedb, b.eth.chainConfig, vmCfg), vmError, nil } @@ -185,7 +186,7 @@ func (b *EthApiBackend) ProtocolVersion() int { } func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(), nil + return b.gpo.SuggestPrice(ctx) } func (b *EthApiBackend) ChainDb() ethdb.Database { diff --git a/eth/backend.go b/eth/backend.go index ef951a6c2..03c2e38e5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -20,14 +20,16 @@ package eth import ( "errors" "fmt" - "math/big" - "regexp" - "strings" + "runtime" "sync" - "time" + "sync/atomic" "github.com/ethereum/go-ethereum/accounts" "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/clique" + "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/core/vm" @@ -42,69 +44,10 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" ) -const ( - epochLength = 30000 - ethashRevision = 23 - - autoDAGcheckInterval = 10 * time.Hour - autoDAGepochHeight = epochLength / 2 -) - -var ( - datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true} - portInUseErrRE = regexp.MustCompile("address already in use") -) - -type Config struct { - ChainConfig *params.ChainConfig // chain configuration - - NetworkId int // Network ID to use for selecting peers to connect to - Genesis string // Genesis JSON to seed the chain database with - FastSync bool // Enables the state download based fast synchronisation algorithm - LightMode bool // Running in light client mode - LightServ int // Maximum percentage of time allowed for serving LES requests - LightPeers int // Maximum number of LES client peers - MaxPeers int // Maximum number of global peers - - SkipBcVersionCheck bool // e.g. blockchain export - DatabaseCache int - DatabaseHandles int - - DocRoot string - PowFake bool - PowTest bool - PowShared bool - ExtraData []byte - - EthashCacheDir string - EthashCachesInMem int - EthashCachesOnDisk int - EthashDatasetDir string - EthashDatasetsInMem int - EthashDatasetsOnDisk int - - Etherbase common.Address - GasPrice *big.Int - MinerThreads int - SolcPath string - - GpoMinGasPrice *big.Int - GpoMaxGasPrice *big.Int - GpoFullBlockRatio int - GpobaseStepDown int - GpobaseStepUp int - GpobaseCorrectionFactor int - - EnablePreimageRecording bool - - TestGenesisBlock *types.Block // Genesis block to seed the chain database with (testing only!) - TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!) -} - type LesServer interface { Start(srvr *p2p.Server) Stop() @@ -127,7 +70,7 @@ type Ethereum struct { chainDb ethdb.Database // Block chain database eventMux *event.TypeMux - pow pow.PoW + engine consensus.Engine accountManager *accounts.Manager ApiBackend *EthApiBackend @@ -136,7 +79,6 @@ type Ethereum struct { Mining bool MinerThreads int etherbase common.Address - solcPath string netVersionId int netRPCService *ethapi.PublicNetAPI @@ -150,25 +92,35 @@ func (s *Ethereum) AddLesServer(ls LesServer) { // New creates a new Ethereum object (including the // initialisation of the common Ethereum object) func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { + if config.SyncMode == downloader.LightSync { + return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum") + } + if !config.SyncMode.IsValid() { + return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) + } + chainDb, err := CreateDB(ctx, config, "chaindata") if err != nil { return nil, err } stopDbUpgrade := upgradeSequentialKeys(chainDb) - if err := SetupGenesisBlock(&chainDb, config); err != nil { - return nil, err + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) + if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { + return nil, genesisErr } + log.Info("Initialised chain configuration", "config", chainConfig) + eth := &Ethereum{ chainDb: chainDb, + chainConfig: chainConfig, eventMux: ctx.EventMux, accountManager: ctx.AccountManager, - pow: CreatePoW(ctx, config), + engine: CreateConsensusEngine(ctx, config, chainConfig, chainDb), shutdownChan: make(chan bool), stopDbUpgrade: stopDbUpgrade, netVersionId: config.NetworkId, etherbase: config.Etherbase, MinerThreads: config.MinerThreads, - solcPath: config.SolcPath, } if err := addMipmapBloomBins(chainDb); err != nil { @@ -184,33 +136,18 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) } - // load the genesis block or write a new one if no genesis - // block is prenent in the database. - genesis := core.GetBlock(chainDb, core.GetCanonicalHash(chainDb, 0), 0) - if genesis == nil { - genesis, err = core.WriteDefaultGenesisBlock(chainDb) - if err != nil { - return nil, err - } - log.Warn("Wrote default Ethereum genesis block") - } - - if config.ChainConfig == nil { - return nil, errors.New("missing chain config") - } - core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig) - - eth.chainConfig = config.ChainConfig - - log.Info("Initialised chain configuration", "config", eth.chainConfig) - - eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux(), vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}) + vmConfig := vm.Config{EnablePreimageRecording: config.EnablePreimageRecording} + eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.engine, eth.eventMux, vmConfig) if err != nil { - if err == core.ErrNoGenesis { - return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`) - } return nil, err } + // Rewind the chain in case of an incompatible config upgrade. + if compat, ok := genesisErr.(*params.ConfigCompatError); ok { + log.Warn("Rewinding chain to upgrade configuration", "err", compat) + eth.blockchain.SetHead(compat.RewindTo) + core.WriteChainConfig(chainDb, genesisHash, chainConfig) + } + newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) eth.txPool = newPool @@ -225,27 +162,41 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } } - if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { + if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil { return nil, err } - eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow) + + eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine) eth.miner.SetGasPrice(config.GasPrice) - eth.miner.SetExtra(config.ExtraData) - - gpoParams := &gasprice.GpoParams{ - GpoMinGasPrice: config.GpoMinGasPrice, - GpoMaxGasPrice: config.GpoMaxGasPrice, - GpoFullBlockRatio: config.GpoFullBlockRatio, - GpobaseStepDown: config.GpobaseStepDown, - GpobaseStepUp: config.GpobaseStepUp, - GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, + eth.miner.SetExtra(makeExtraData(config.ExtraData)) + + eth.ApiBackend = &EthApiBackend{eth, nil} + gpoParams := config.GPO + if gpoParams.Default == nil { + gpoParams.Default = config.GasPrice } - gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) - eth.ApiBackend = &EthApiBackend{eth, gpo} + eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams) return eth, nil } +func makeExtraData(extra []byte) []byte { + if len(extra) == 0 { + // create default extradata + extra, _ = rlp.EncodeToBytes([]interface{}{ + uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), + "geth", + runtime.Version(), + runtime.GOOS, + }) + } + if uint64(len(extra)) > params.MaximumExtraDataSize { + log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize) + extra = nil + } + return extra +} + // CreateDB creates the chain database. func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) { db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles) @@ -255,51 +206,41 @@ func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Data return db, err } -// SetupGenesisBlock initializes the genesis block for an Ethereum service -func SetupGenesisBlock(chainDb *ethdb.Database, config *Config) error { - // Load up any custom genesis block if requested - if len(config.Genesis) > 0 { - block, err := core.WriteGenesisBlock(*chainDb, strings.NewReader(config.Genesis)) - if err != nil { - return err - } - log.Info("Successfully wrote custom genesis block", "hash", block.Hash()) - } - // Load up a test setup if directly injected - if config.TestGenesisState != nil { - *chainDb = config.TestGenesisState +// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service +func CreateConsensusEngine(ctx *node.ServiceContext, config *Config, chainConfig *params.ChainConfig, db ethdb.Database) consensus.Engine { + // If proof-of-authority is requested, set it up + if chainConfig.Clique != nil { + return clique.New(chainConfig.Clique, db) } - if config.TestGenesisBlock != nil { - core.WriteTd(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty()) - core.WriteBlock(*chainDb, config.TestGenesisBlock) - core.WriteCanonicalHash(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64()) - core.WriteHeadBlockHash(*chainDb, config.TestGenesisBlock.Hash()) - } - return nil -} - -// CreatePoW creates the required type of PoW instance for an Ethereum service -func CreatePoW(ctx *node.ServiceContext, config *Config) pow.PoW { + // Otherwise assume proof-of-work switch { case config.PowFake: log.Warn("Ethash used in fake mode") - return pow.FakePow{} + return ethash.NewFaker() case config.PowTest: log.Warn("Ethash used in test mode") - return pow.NewTestEthash() + return ethash.NewTester() case config.PowShared: log.Warn("Ethash used in shared mode") - return pow.NewSharedEthash() + return ethash.NewShared() default: - return pow.NewFullEthash(ctx.ResolvePath(config.EthashCacheDir), config.EthashCachesInMem, config.EthashCachesOnDisk, + engine := ethash.New(ctx.ResolvePath(config.EthashCacheDir), config.EthashCachesInMem, config.EthashCachesOnDisk, config.EthashDatasetDir, config.EthashDatasetsInMem, config.EthashDatasetsOnDisk) + engine.SetThreads(-1) // Disable CPU mining + return engine } } // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *Ethereum) APIs() []rpc.API { - return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ + apis := ethapi.GetAPIs(s.ApiBackend) + + // Append any APIs exposed explicitly by the consensus engine + apis = append(apis, s.engine.APIs(s.BlockChain())...) + + // Append all the local APIs and return + return append(apis, []rpc.API{ { Namespace: "eth", Version: "1.0", @@ -369,13 +310,28 @@ func (self *Ethereum) SetEtherbase(etherbase common.Address) { self.miner.SetEtherbase(etherbase) } -func (s *Ethereum) StartMining(threads int) error { +func (s *Ethereum) StartMining(local bool) error { eb, err := s.Etherbase() if err != nil { log.Error("Cannot start mining without etherbase", "err", err) return fmt.Errorf("etherbase missing: %v", err) } - go s.miner.Start(eb, threads) + if clique, ok := s.engine.(*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("singer missing: %v", err) + } + clique.Authorize(eb, wallet.SignHash) + } + if local { + // If local (CPU) mining is started, we can disable the transaction rejection + // mechanism introduced to speed sync times. CPU mining on mainnet is ludicrous + // so noone will ever hit this path, whereas marking sync done on CPU mining + // will ensure that private networks work in single miner mode too. + atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) + } + go s.miner.Start(eb) return nil } @@ -387,7 +343,7 @@ func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } -func (s *Ethereum) Pow() pow.PoW { return s.pow } +func (s *Ethereum) Engine() consensus.Engine { return s.engine } func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } @@ -436,8 +392,3 @@ func (s *Ethereum) Stop() error { return nil } - -// This function will wait for a shutdown and resumes main thread execution -func (s *Ethereum) WaitForShutdown() { - <-s.shutdownChan -} diff --git a/eth/backend_test.go b/eth/backend_test.go index 574731fbe..f60e3214c 100644 --- a/eth/backend_test.go +++ b/eth/backend_test.go @@ -30,7 +30,7 @@ import ( func TestMipmapUpgrade(t *testing.T) { db, _ := ethdb.NewMemDatabase() addr := common.BytesToAddress([]byte("jeff")) - genesis := core.WriteGenesisBlockForTesting(db) + genesis := new(core.Genesis).MustCommit(db) chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) { var receipts types.Receipts diff --git a/eth/bad_block.go b/eth/bad_block.go deleted file mode 100644 index dd1ced804..000000000 --- a/eth/bad_block.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package eth - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" -) - -const ( - // The Ethereum main network genesis block. - defaultGenesisHash = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" - badBlocksURL = "https://badblocks.ethdev.com" -) - -var EnableBadBlockReporting = false - -func sendBadBlockReport(block *types.Block, err error) { - if !EnableBadBlockReporting { - return - } - - var ( - blockRLP, _ = rlp.EncodeToBytes(block) - params = map[string]interface{}{ - "block": common.Bytes2Hex(blockRLP), - "blockHash": block.Hash().Hex(), - "errortype": err.Error(), - "client": "go", - } - ) - if !block.ReceivedAt.IsZero() { - params["receivedAt"] = block.ReceivedAt.UTC().String() - } - if p, ok := block.ReceivedFrom.(*peer); ok { - params["receivedFrom"] = map[string]interface{}{ - "enode": fmt.Sprintf("enode://%x@%v", p.ID(), p.RemoteAddr()), - "name": p.Name(), - "protocolVersion": p.version, - } - } - jsonStr, _ := json.Marshal(map[string]interface{}{"method": "eth_badBlock", "id": "1", "jsonrpc": "2.0", "params": []interface{}{params}}) - client := http.Client{Timeout: 8 * time.Second} - resp, err := client.Post(badBlocksURL, "application/json", bytes.NewReader(jsonStr)) - if err != nil { - log.Debug("Failed to report bad block", "err", err) - return - } - log.Debug("Bad block report posted", "status", resp.StatusCode) - resp.Body.Close() -} diff --git a/eth/bind.go b/eth/bind.go index 2ee9f2bf7..245934183 100644 --- a/eth/bind.go +++ b/eth/bind.go @@ -17,6 +17,7 @@ package eth import ( + "context" "math/big" "github.com/ethereum/go-ethereum" @@ -26,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) // ContractBackend implements bind.ContractBackend with direct calls to Ethereum diff --git a/eth/config.go b/eth/config.go new file mode 100644 index 000000000..daa402ca5 --- /dev/null +++ b/eth/config.go @@ -0,0 +1,116 @@ +// Copyright 2014 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 <http://www.gnu.org/licenses/>. + +package eth + +import ( + "math/big" + "os" + "os/user" + "path/filepath" + "runtime" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/params" +) + +// DefaultConfig contains default settings for use on the Ethereum main net. +var DefaultConfig = Config{ + SyncMode: downloader.FastSync, + EthashCachesInMem: 2, + EthashCachesOnDisk: 3, + EthashDatasetsInMem: 1, + EthashDatasetsOnDisk: 2, + NetworkId: 1, + LightPeers: 20, + DatabaseCache: 128, + GasPrice: big.NewInt(20 * params.Shannon), + + GPO: gasprice.Config{ + Blocks: 10, + Percentile: 50, + }, +} + +func init() { + home := os.Getenv("HOME") + if home == "" { + if user, err := user.Current(); err == nil { + home = user.HomeDir + } + } + if runtime.GOOS == "windows" { + DefaultConfig.EthashDatasetDir = filepath.Join(home, "AppData", "Ethash") + } else { + DefaultConfig.EthashDatasetDir = filepath.Join(home, ".ethash") + } +} + +//go:generate gencodec -type Config -field-override configMarshaling -formats toml -out gen_config.go + +type Config struct { + // The genesis block, which is inserted if the database is empty. + // If nil, the Ethereum main net block is used. + Genesis *core.Genesis `toml:",omitempty"` + + // Protocol options + NetworkId int // Network ID to use for selecting peers to connect to + SyncMode downloader.SyncMode + + // Light client options + LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests + LightPeers int `toml:",omitempty"` // Maximum number of LES client peers + MaxPeers int `toml:"-"` // Maximum number of global peers + + // Database options + SkipBcVersionCheck bool `toml:"-"` + DatabaseHandles int `toml:"-"` + DatabaseCache int + + // Mining-related options + Etherbase common.Address `toml:",omitempty"` + MinerThreads int `toml:",omitempty"` + ExtraData []byte `toml:",omitempty"` + GasPrice *big.Int + + // Ethash options + EthashCacheDir string + EthashCachesInMem int + EthashCachesOnDisk int + EthashDatasetDir string + EthashDatasetsInMem int + EthashDatasetsOnDisk int + + // Gas Price Oracle options + GPO gasprice.Config + + // Enables tracking of SHA3 preimages in the VM + EnablePreimageRecording bool + + // Miscellaneous options + DocRoot string `toml:"-"` + PowFake bool `toml:"-"` + PowTest bool `toml:"-"` + PowShared bool `toml:"-"` +} + +type configMarshaling struct { + ExtraData hexutil.Bytes +} diff --git a/eth/downloader/api.go b/eth/downloader/api.go index e41376810..d496fa6a4 100644 --- a/eth/downloader/api.go +++ b/eth/downloader/api.go @@ -17,12 +17,12 @@ package downloader import ( + "context" "sync" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) // PublicDownloaderAPI provides an API which gives information about the current synchronisation status. diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index f7aca031a..d26995782 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -277,7 +277,7 @@ func (d *Downloader) UnregisterPeer(id string) error { d.cancelLock.RUnlock() if master { - d.cancel() + d.Cancel() } return nil } @@ -352,7 +352,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode d.cancelPeer = id d.cancelLock.Unlock() - defer d.cancel() // No matter what, we can't leave the cancel channel open + defer d.Cancel() // No matter what, we can't leave the cancel channel open // Set the requested sync mode, unless it's forbidden d.mode = mode @@ -473,7 +473,7 @@ func (d *Downloader) spawnSync(origin uint64, fetchers ...func() error) error { } } d.queue.Close() - d.cancel() + d.Cancel() wg.Wait() // If sync failed in the critical section, bump the fail counter @@ -483,9 +483,9 @@ func (d *Downloader) spawnSync(origin uint64, fetchers ...func() error) error { return err } -// cancel cancels all of the operations and resets the queue. It returns true +// Cancel cancels all of the operations and resets the queue. It returns true // if the cancel operation was completed. -func (d *Downloader) cancel() { +func (d *Downloader) Cancel() { // Close the current cancel channel d.cancelLock.Lock() if d.cancelCh != nil { @@ -512,7 +512,7 @@ func (d *Downloader) Terminate() { d.quitLock.Unlock() // Cancel any pending download requests - d.cancel() + d.Cancel() } // fetchHeight retrieves the head header of the remote peer to aid in estimating @@ -945,7 +945,7 @@ func (d *Downloader) fetchNodeData() error { if err != nil { // If the node data processing failed, the root hash is very wrong, abort log.Error("State processing failed", "peer", packet.PeerId(), "err", err) - d.cancel() + d.Cancel() return } // Processing succeeded, notify state fetcher of continuation @@ -1208,7 +1208,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { if atomic.LoadUint32(&d.fsPivotFails) == 0 { for _, header := range rollback { if header.Number.Uint64() == pivot { - log.Warn("Fast-sync critical section failure, locked pivot to header", "number", pivot, "hash", header.Hash()) + log.Warn("Fast-sync pivot locked in", "number", pivot, "hash", header.Hash()) d.fsPivotLock = header } } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index a9ea797ea..267a0def9 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -982,7 +982,7 @@ func testCancel(t *testing.T, protocol int, mode SyncMode) { tester.newPeer("peer", protocol, hashes, headers, blocks, receipts) // Make sure canceling works with a pristine downloader - tester.downloader.cancel() + tester.downloader.Cancel() if !tester.downloader.queue.Idle() { t.Errorf("download queue not idle") } @@ -990,7 +990,7 @@ func testCancel(t *testing.T, protocol int, mode SyncMode) { if err := tester.sync("peer", nil, mode); err != nil { t.Fatalf("failed to synchronise blocks: %v", err) } - tester.downloader.cancel() + tester.downloader.Cancel() if !tester.downloader.queue.Idle() { t.Errorf("download queue not idle") } diff --git a/eth/downloader/modes.go b/eth/downloader/modes.go index ae3c43888..8ecdf91f1 100644 --- a/eth/downloader/modes.go +++ b/eth/downloader/modes.go @@ -16,6 +16,8 @@ package downloader +import "fmt" + // SyncMode represents the synchronisation mode of the downloader. type SyncMode int @@ -25,6 +27,10 @@ const ( LightSync // Download only the headers and terminate afterwards ) +func (mode SyncMode) IsValid() bool { + return mode >= FullSync && mode <= LightSync +} + // String implements the stringer interface. func (mode SyncMode) String() string { switch mode { @@ -38,3 +44,30 @@ func (mode SyncMode) String() string { return "unknown" } } + +func (mode SyncMode) MarshalText() ([]byte, error) { + switch mode { + case FullSync: + return []byte("full"), nil + case FastSync: + return []byte("fast"), nil + case LightSync: + return []byte("light"), nil + default: + return nil, fmt.Errorf("unknown sync mode %d", mode) + } +} + +func (mode *SyncMode) UnmarshalText(text []byte) error { + switch string(text) { + case "full": + *mode = FullSync + case "fast": + *mode = FastSync + case "light": + *mode = LightSync + default: + return fmt.Errorf(`unknown sync mode %q, want "full", "fast" or "light"`, text) + } + return nil +} diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go index d82f4f3e6..98cc1a76b 100644 --- a/eth/fetcher/fetcher.go +++ b/eth/fetcher/fetcher.go @@ -23,7 +23,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "gopkg.in/karalabe/cookiejar.v2/collections/prque" @@ -52,8 +52,8 @@ type headerRequesterFn func(common.Hash) error // bodyRequesterFn is a callback type for sending a body retrieval request. type bodyRequesterFn func([]common.Hash) error -// blockValidatorFn is a callback type to verify a block's header for fast propagation. -type blockValidatorFn func(block *types.Block, parent *types.Block) error +// headerVerifierFn is a callback type to verify a block's header for fast propagation. +type headerVerifierFn func(header *types.Header) error // blockBroadcasterFn is a callback type for broadcasting a block to connected peers. type blockBroadcasterFn func(block *types.Block, propagate bool) @@ -129,7 +129,7 @@ type Fetcher struct { // Callbacks getBlock blockRetrievalFn // Retrieves a block from the local chain - validateBlock blockValidatorFn // Checks if a block's headers have a valid proof of work + verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers chainHeight chainHeightFn // Retrieves the current chain's height insertChain chainInsertFn // Injects a batch of blocks into the chain @@ -144,7 +144,7 @@ type Fetcher struct { } // New creates a block fetcher to retrieve blocks based on hash announcements. -func New(getBlock blockRetrievalFn, validateBlock blockValidatorFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertChain chainInsertFn, dropPeer peerDropFn) *Fetcher { +func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertChain chainInsertFn, dropPeer peerDropFn) *Fetcher { return &Fetcher{ notify: make(chan *announce), inject: make(chan *inject), @@ -162,7 +162,7 @@ func New(getBlock blockRetrievalFn, validateBlock blockValidatorFn, broadcastBlo queues: make(map[string]int), queued: make(map[common.Hash]*inject), getBlock: getBlock, - validateBlock: validateBlock, + verifyHeader: verifyHeader, broadcastBlock: broadcastBlock, chainHeight: chainHeight, insertChain: insertChain, @@ -648,13 +648,13 @@ func (f *Fetcher) insert(peer string, block *types.Block) { return } // Quickly validate the header and propagate the block if it passes - switch err := f.validateBlock(block, parent); err { + switch err := f.verifyHeader(block.Header()); err { case nil: // All ok, quickly propagate to our peers propBroadcastOutTimer.UpdateSince(block.ReceivedAt) go f.broadcastBlock(block, true) - case core.BlockFutureErr: + case consensus.ErrFutureBlock: // Weird future block, don't fail, but neither propagate default: diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go index 7a94241c6..85d2f8645 100644 --- a/eth/fetcher/fetcher_test.go +++ b/eth/fetcher/fetcher_test.go @@ -91,7 +91,7 @@ func newTester() *fetcherTester { blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis}, drops: make(map[string]bool), } - tester.fetcher = New(tester.getBlock, tester.verifyBlock, tester.broadcastBlock, tester.chainHeight, tester.insertChain, tester.dropPeer) + tester.fetcher = New(tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertChain, tester.dropPeer) tester.fetcher.Start() return tester @@ -105,8 +105,8 @@ func (f *fetcherTester) getBlock(hash common.Hash) *types.Block { return f.blocks[hash] } -// verifyBlock is a nop placeholder for the block header verification. -func (f *fetcherTester) verifyBlock(block *types.Block, parent *types.Block) error { +// verifyHeader is a nop placeholder for the block header verification. +func (f *fetcherTester) verifyHeader(header *types.Header) error { return nil } diff --git a/eth/filters/api.go b/eth/filters/api.go index 02a544ce1..61647a5d0 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -17,6 +17,7 @@ package filters import ( + "context" "encoding/json" "errors" "fmt" @@ -24,8 +25,6 @@ import ( "sync" "time" - "golang.org/x/net/context" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 9a8e2fd70..0a0b81224 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -17,10 +17,10 @@ package filters import ( + "context" "math" - "time" - "math/big" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) type Backend interface { diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 3adf8111a..7abace1e6 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -19,6 +19,7 @@ package filters import ( + "context" "errors" "fmt" "sync" @@ -29,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) // Type determines the kind of filter and is used to put the filter in to @@ -372,7 +372,8 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*types.Log { if bloomFilter(header.Bloom, addresses, topics) { // Get the logs of the block - ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() receipts, err := es.backend.GetReceipts(ctx, header.Hash()) if err != nil { return nil diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 1cfced7e4..822580b56 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -17,13 +17,12 @@ package filters import ( + "context" "math/big" "reflect" "testing" "time" - "golang.org/x/net/context" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -73,12 +72,11 @@ func TestBlockSubscription(t *testing.T) { t.Parallel() var ( - mux = new(event.TypeMux) - db, _ = ethdb.NewMemDatabase() - backend = &testBackend{mux, db} - api = NewPublicFilterAPI(backend, false) - - genesis = core.WriteGenesisBlockForTesting(db) + mux = new(event.TypeMux) + db, _ = ethdb.NewMemDatabase() + backend = &testBackend{mux, db} + api = NewPublicFilterAPI(backend, false) + genesis = new(core.Genesis).MustCommit(db) chain, _ = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {}) chainEvents = []core.ChainEvent{} ) diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index 83ff3e9ce..cd5e7cafd 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -17,13 +17,12 @@ package filters import ( + "context" "io/ioutil" "math/big" "os" "testing" - "golang.org/x/net/context" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" @@ -61,7 +60,7 @@ func BenchmarkMipmaps(b *testing.B) { ) defer db.Close() - genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000)}) + genesis := core.GenesisBlockForTesting(db, addr1, big.NewInt(1000000)) chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 100010, func(i int, gen *core.BlockGen) { var receipts types.Receipts switch i { @@ -113,7 +112,7 @@ func BenchmarkMipmaps(b *testing.B) { for i := 0; i < b.N; i++ { logs, _ := filter.Find(context.Background()) if len(logs) != 4 { - b.Fatal("expected 4 log, got", len(logs)) + b.Fatal("expected 4 logs, got", len(logs)) } } } @@ -139,7 +138,7 @@ func TestFilters(t *testing.T) { ) defer db.Close() - genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000)}) + genesis := core.GenesisBlockForTesting(db, addr, big.NewInt(1000000)) chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 1000, func(i int, gen *core.BlockGen) { var receipts types.Receipts switch i { diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 73951bce9..05c25e644 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// Copyright 2016 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -17,212 +17,158 @@ package gasprice import ( + "context" "math/big" - "math/rand" + "sort" "sync" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" ) -const ( - gpoProcessPastBlocks = 100 +var maxPrice = big.NewInt(500 * params.Shannon) - // for testing - gpoDefaultBaseCorrectionFactor = 110 - gpoDefaultMinGasPrice = 10000000000000 -) - -type blockPriceInfo struct { - baseGasPrice *big.Int -} - -type GpoParams struct { - GpoMinGasPrice *big.Int - GpoMaxGasPrice *big.Int - GpoFullBlockRatio int - GpobaseStepDown int - GpobaseStepUp int - GpobaseCorrectionFactor int +type Config struct { + Blocks int + Percentile int + Default *big.Int `toml:",omitempty"` } -// GasPriceOracle recommends gas prices based on the content of recent -// blocks. -type GasPriceOracle struct { - chain *core.BlockChain - db ethdb.Database - evmux *event.TypeMux - params *GpoParams - initOnce sync.Once - minPrice *big.Int - lastBaseMutex sync.Mutex - lastBase *big.Int - - // state of listenLoop - blocks map[uint64]*blockPriceInfo - firstProcessed, lastProcessed uint64 - minBase *big.Int +// Oracle recommends gas prices based on the content of recent +// blocks. Suitable for both light and full clients. +type Oracle struct { + backend ethapi.Backend + lastHead common.Hash + lastPrice *big.Int + cacheLock sync.RWMutex + fetchLock sync.Mutex + + checkBlocks, maxEmpty, maxBlocks int + percentile int } -// NewGasPriceOracle returns a new oracle. -func NewGasPriceOracle(chain *core.BlockChain, db ethdb.Database, evmux *event.TypeMux, params *GpoParams) *GasPriceOracle { - minprice := params.GpoMinGasPrice - if minprice == nil { - minprice = big.NewInt(gpoDefaultMinGasPrice) +// NewOracle returns a new oracle. +func NewOracle(backend ethapi.Backend, params Config) *Oracle { + blocks := params.Blocks + if blocks < 1 { + blocks = 1 } - minbase := new(big.Int).Mul(minprice, big.NewInt(100)) - if params.GpobaseCorrectionFactor > 0 { - minbase = minbase.Div(minbase, big.NewInt(int64(params.GpobaseCorrectionFactor))) + percent := params.Percentile + if percent < 0 { + percent = 0 } - return &GasPriceOracle{ - chain: chain, - db: db, - evmux: evmux, - params: params, - blocks: make(map[uint64]*blockPriceInfo), - minBase: minbase, - minPrice: minprice, - lastBase: minprice, + if percent > 100 { + percent = 100 } -} - -func (gpo *GasPriceOracle) init() { - gpo.initOnce.Do(func() { - gpo.processPastBlocks() - go gpo.listenLoop() - }) -} - -func (self *GasPriceOracle) processPastBlocks() { - last := int64(-1) - cblock := self.chain.CurrentBlock() - if cblock != nil { - last = int64(cblock.NumberU64()) - } - first := int64(0) - if last > gpoProcessPastBlocks { - first = last - gpoProcessPastBlocks - } - self.firstProcessed = uint64(first) - for i := first; i <= last; i++ { - block := self.chain.GetBlockByNumber(uint64(i)) - if block != nil { - self.processBlock(block) - } + return &Oracle{ + backend: backend, + lastPrice: params.Default, + checkBlocks: blocks, + maxEmpty: blocks / 2, + maxBlocks: blocks * 5, + percentile: percent, } - } -func (self *GasPriceOracle) listenLoop() { - events := self.evmux.Subscribe(core.ChainEvent{}, core.ChainSplitEvent{}) - defer events.Unsubscribe() - - for event := range events.Chan() { - switch event := event.Data.(type) { - case core.ChainEvent: - self.processBlock(event.Block) - case core.ChainSplitEvent: - self.processBlock(event.Block) +// SuggestPrice returns the recommended gas price. +func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { + gpo.cacheLock.RLock() + lastHead := gpo.lastHead + lastPrice := gpo.lastPrice + gpo.cacheLock.RUnlock() + + head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + headHash := head.Hash() + if headHash == lastHead { + return lastPrice, nil + } + + gpo.fetchLock.Lock() + defer gpo.fetchLock.Unlock() + + // try checking the cache again, maybe the last fetch fetched what we need + gpo.cacheLock.RLock() + lastHead = gpo.lastHead + lastPrice = gpo.lastPrice + gpo.cacheLock.RUnlock() + if headHash == lastHead { + return lastPrice, nil + } + + blockNum := head.Number.Uint64() + ch := make(chan getBlockPricesResult, gpo.checkBlocks) + sent := 0 + exp := 0 + var txPrices []*big.Int + for sent < gpo.checkBlocks && blockNum > 0 { + go gpo.getBlockPrices(ctx, blockNum, ch) + sent++ + exp++ + blockNum-- + } + maxEmpty := gpo.maxEmpty + for exp > 0 { + res := <-ch + if res.err != nil { + return lastPrice, res.err + } + exp-- + if len(res.prices) > 0 { + txPrices = append(txPrices, res.prices...) + continue + } + if maxEmpty > 0 { + maxEmpty-- + continue + } + if blockNum > 0 && sent < gpo.maxBlocks { + go gpo.getBlockPrices(ctx, blockNum, ch) + sent++ + exp++ + blockNum-- } } -} - -func (self *GasPriceOracle) processBlock(block *types.Block) { - i := block.NumberU64() - if i > self.lastProcessed { - self.lastProcessed = i - } - - lastBase := self.minPrice - bpl := self.blocks[i-1] - if bpl != nil { - lastBase = bpl.baseGasPrice - } - if lastBase == nil { - return - } - - var corr int - lp := self.lowestPrice(block) - if lp == nil { - return - } - - if lastBase.Cmp(lp) < 0 { - corr = self.params.GpobaseStepUp - } else { - corr = -self.params.GpobaseStepDown - } - - crand := int64(corr * (900 + rand.Intn(201))) - newBase := new(big.Int).Mul(lastBase, big.NewInt(1000000+crand)) - newBase.Div(newBase, big.NewInt(1000000)) - - if newBase.Cmp(self.minBase) < 0 { - newBase = self.minBase + price := lastPrice + if len(txPrices) > 0 { + sort.Sort(bigIntArray(txPrices)) + price = txPrices[(len(txPrices)-1)*gpo.percentile/100] } - - bpi := self.blocks[i] - if bpi == nil { - bpi = &blockPriceInfo{} - self.blocks[i] = bpi + if price.Cmp(maxPrice) > 0 { + price = new(big.Int).Set(maxPrice) } - bpi.baseGasPrice = newBase - self.lastBaseMutex.Lock() - self.lastBase = newBase - self.lastBaseMutex.Unlock() - log.Trace("Processed block, base price updated", "number", i, "base", newBase) + gpo.cacheLock.Lock() + gpo.lastHead = headHash + gpo.lastPrice = price + gpo.cacheLock.Unlock() + return price, nil } -// returns the lowers possible price with which a tx was or could have been included -func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { - gasUsed := big.NewInt(0) - - receipts := core.GetBlockReceipts(self.db, block.Hash(), block.NumberU64()) - if len(receipts) > 0 { - if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil { - gasUsed = receipts[len(receipts)-1].CumulativeGasUsed - } - } +type getBlockPricesResult struct { + prices []*big.Int + err error +} - if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(), - big.NewInt(int64(self.params.GpoFullBlockRatio)))) < 0 { - // block is not full, could have posted a tx with MinGasPrice - return big.NewInt(0) +// getLowestPrice calculates the lowest transaction gas price in a given block +// and sends it to the result channel. If the block is empty, price is nil. +func (gpo *Oracle) getBlockPrices(ctx context.Context, blockNum uint64, ch chan getBlockPricesResult) { + block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) + if block == nil { + ch <- getBlockPricesResult{nil, err} + return } - txs := block.Transactions() - if len(txs) == 0 { - return big.NewInt(0) + prices := make([]*big.Int, len(txs)) + for i, tx := range txs { + prices[i] = tx.GasPrice() } - // block is full, find smallest gasPrice - minPrice := txs[0].GasPrice() - for i := 1; i < len(txs); i++ { - price := txs[i].GasPrice() - if price.Cmp(minPrice) < 0 { - minPrice = price - } - } - return minPrice + ch <- getBlockPricesResult{prices, nil} } -// SuggestPrice returns the recommended gas price. -func (self *GasPriceOracle) SuggestPrice() *big.Int { - self.init() - self.lastBaseMutex.Lock() - price := new(big.Int).Set(self.lastBase) - self.lastBaseMutex.Unlock() - - price.Mul(price, big.NewInt(int64(self.params.GpobaseCorrectionFactor))) - price.Div(price, big.NewInt(100)) - if price.Cmp(self.minPrice) < 0 { - price.Set(self.minPrice) - } else if self.params.GpoMaxGasPrice != nil && price.Cmp(self.params.GpoMaxGasPrice) > 0 { - price.Set(self.params.GpoMaxGasPrice) - } - return price -} +type bigIntArray []*big.Int + +func (s bigIntArray) Len() int { return len(s) } +func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } +func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/eth/gasprice/lightprice.go b/eth/gasprice/lightprice.go deleted file mode 100644 index 8886d32d7..000000000 --- a/eth/gasprice/lightprice.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -package gasprice - -import ( - "math/big" - "sort" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" -) - -const ( - LpoAvgCount = 5 - LpoMinCount = 3 - LpoMaxBlocks = 20 - LpoSelect = 50 - LpoDefaultPrice = 20000000000 -) - -// LightPriceOracle recommends gas prices based on the content of recent -// blocks. Suitable for both light and full clients. -type LightPriceOracle struct { - backend ethapi.Backend - lastHead common.Hash - lastPrice *big.Int - cacheLock sync.RWMutex - fetchLock sync.Mutex -} - -// NewLightPriceOracle returns a new oracle. -func NewLightPriceOracle(backend ethapi.Backend) *LightPriceOracle { - return &LightPriceOracle{ - backend: backend, - lastPrice: big.NewInt(LpoDefaultPrice), - } -} - -// SuggestPrice returns the recommended gas price. -func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error) { - self.cacheLock.RLock() - lastHead := self.lastHead - lastPrice := self.lastPrice - self.cacheLock.RUnlock() - - head, _ := self.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) - headHash := head.Hash() - if headHash == lastHead { - return lastPrice, nil - } - - self.fetchLock.Lock() - defer self.fetchLock.Unlock() - - // try checking the cache again, maybe the last fetch fetched what we need - self.cacheLock.RLock() - lastHead = self.lastHead - lastPrice = self.lastPrice - self.cacheLock.RUnlock() - if headHash == lastHead { - return lastPrice, nil - } - - blockNum := head.Number.Uint64() - chn := make(chan lpResult, LpoMaxBlocks) - sent := 0 - exp := 0 - var lps bigIntArray - for sent < LpoAvgCount && blockNum > 0 { - go self.getLowestPrice(ctx, blockNum, chn) - sent++ - exp++ - blockNum-- - } - maxEmpty := LpoAvgCount - LpoMinCount - for exp > 0 { - res := <-chn - if res.err != nil { - return nil, res.err - } - exp-- - if res.price != nil { - lps = append(lps, res.price) - } else { - if maxEmpty > 0 { - maxEmpty-- - } else { - if blockNum > 0 && sent < LpoMaxBlocks { - go self.getLowestPrice(ctx, blockNum, chn) - sent++ - exp++ - blockNum-- - } - } - } - } - price := lastPrice - if len(lps) > 0 { - sort.Sort(lps) - price = lps[(len(lps)-1)*LpoSelect/100] - } - - self.cacheLock.Lock() - self.lastHead = headHash - self.lastPrice = price - self.cacheLock.Unlock() - return price, nil -} - -type lpResult struct { - price *big.Int - err error -} - -// getLowestPrice calculates the lowest transaction gas price in a given block -// and sends it to the result channel. If the block is empty, price is nil. -func (self *LightPriceOracle) getLowestPrice(ctx context.Context, blockNum uint64, chn chan lpResult) { - block, err := self.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) - if block == nil { - chn <- lpResult{nil, err} - return - } - txs := block.Transactions() - if len(txs) == 0 { - chn <- lpResult{nil, nil} - return - } - // find smallest gasPrice - minPrice := txs[0].GasPrice() - for i := 1; i < len(txs); i++ { - price := txs[i].GasPrice() - if price.Cmp(minPrice) < 0 { - minPrice = price - } - } - chn <- lpResult{minPrice, nil} -} - -type bigIntArray []*big.Int - -func (s bigIntArray) Len() int { return len(s) } -func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 } -func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/eth/gen_config.go b/eth/gen_config.go new file mode 100644 index 000000000..56fba1d89 --- /dev/null +++ b/eth/gen_config.go @@ -0,0 +1,180 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package eth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/gasprice" +) + +func (c Config) MarshalTOML() (interface{}, error) { + type Config struct { + Genesis *core.Genesis `toml:",omitempty"` + NetworkId int + SyncMode downloader.SyncMode + LightServ int `toml:",omitempty"` + LightPeers int `toml:",omitempty"` + MaxPeers int `toml:"-"` + SkipBcVersionCheck bool `toml:"-"` + DatabaseHandles int `toml:"-"` + DatabaseCache int + Etherbase common.Address `toml:",omitempty"` + MinerThreads int `toml:",omitempty"` + ExtraData hexutil.Bytes `toml:",omitempty"` + GasPrice *big.Int + EthashCacheDir string + EthashCachesInMem int + EthashCachesOnDisk int + EthashDatasetDir string + EthashDatasetsInMem int + EthashDatasetsOnDisk int + GPO gasprice.Config + EnablePreimageRecording bool + DocRoot string `toml:"-"` + PowFake bool `toml:"-"` + PowTest bool `toml:"-"` + PowShared bool `toml:"-"` + } + var enc Config + enc.Genesis = c.Genesis + enc.NetworkId = c.NetworkId + enc.SyncMode = c.SyncMode + enc.LightServ = c.LightServ + enc.LightPeers = c.LightPeers + enc.MaxPeers = c.MaxPeers + enc.SkipBcVersionCheck = c.SkipBcVersionCheck + enc.DatabaseHandles = c.DatabaseHandles + enc.DatabaseCache = c.DatabaseCache + enc.Etherbase = c.Etherbase + enc.MinerThreads = c.MinerThreads + enc.ExtraData = c.ExtraData + enc.GasPrice = c.GasPrice + enc.EthashCacheDir = c.EthashCacheDir + enc.EthashCachesInMem = c.EthashCachesInMem + enc.EthashCachesOnDisk = c.EthashCachesOnDisk + enc.EthashDatasetDir = c.EthashDatasetDir + enc.EthashDatasetsInMem = c.EthashDatasetsInMem + enc.EthashDatasetsOnDisk = c.EthashDatasetsOnDisk + enc.GPO = c.GPO + enc.EnablePreimageRecording = c.EnablePreimageRecording + enc.DocRoot = c.DocRoot + enc.PowFake = c.PowFake + enc.PowTest = c.PowTest + enc.PowShared = c.PowShared + return &enc, nil +} + +func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { + type Config struct { + Genesis *core.Genesis `toml:",omitempty"` + NetworkId *int + SyncMode *downloader.SyncMode + LightServ *int `toml:",omitempty"` + LightPeers *int `toml:",omitempty"` + MaxPeers *int `toml:"-"` + SkipBcVersionCheck *bool `toml:"-"` + DatabaseHandles *int `toml:"-"` + DatabaseCache *int + Etherbase *common.Address `toml:",omitempty"` + MinerThreads *int `toml:",omitempty"` + ExtraData hexutil.Bytes `toml:",omitempty"` + GasPrice *big.Int + EthashCacheDir *string + EthashCachesInMem *int + EthashCachesOnDisk *int + EthashDatasetDir *string + EthashDatasetsInMem *int + EthashDatasetsOnDisk *int + GPO *gasprice.Config + EnablePreimageRecording *bool + DocRoot *string `toml:"-"` + PowFake *bool `toml:"-"` + PowTest *bool `toml:"-"` + PowShared *bool `toml:"-"` + } + var dec Config + if err := unmarshal(&dec); err != nil { + return err + } + if dec.Genesis != nil { + c.Genesis = dec.Genesis + } + if dec.NetworkId != nil { + c.NetworkId = *dec.NetworkId + } + if dec.SyncMode != nil { + c.SyncMode = *dec.SyncMode + } + if dec.LightServ != nil { + c.LightServ = *dec.LightServ + } + if dec.LightPeers != nil { + c.LightPeers = *dec.LightPeers + } + if dec.MaxPeers != nil { + c.MaxPeers = *dec.MaxPeers + } + if dec.SkipBcVersionCheck != nil { + c.SkipBcVersionCheck = *dec.SkipBcVersionCheck + } + if dec.DatabaseHandles != nil { + c.DatabaseHandles = *dec.DatabaseHandles + } + if dec.DatabaseCache != nil { + c.DatabaseCache = *dec.DatabaseCache + } + if dec.Etherbase != nil { + c.Etherbase = *dec.Etherbase + } + if dec.MinerThreads != nil { + c.MinerThreads = *dec.MinerThreads + } + if dec.ExtraData != nil { + c.ExtraData = dec.ExtraData + } + if dec.GasPrice != nil { + c.GasPrice = dec.GasPrice + } + if dec.EthashCacheDir != nil { + c.EthashCacheDir = *dec.EthashCacheDir + } + if dec.EthashCachesInMem != nil { + c.EthashCachesInMem = *dec.EthashCachesInMem + } + if dec.EthashCachesOnDisk != nil { + c.EthashCachesOnDisk = *dec.EthashCachesOnDisk + } + if dec.EthashDatasetDir != nil { + c.EthashDatasetDir = *dec.EthashDatasetDir + } + if dec.EthashDatasetsInMem != nil { + c.EthashDatasetsInMem = *dec.EthashDatasetsInMem + } + if dec.EthashDatasetsOnDisk != nil { + c.EthashDatasetsOnDisk = *dec.EthashDatasetsOnDisk + } + if dec.GPO != nil { + c.GPO = *dec.GPO + } + if dec.EnablePreimageRecording != nil { + c.EnablePreimageRecording = *dec.EnablePreimageRecording + } + if dec.DocRoot != nil { + c.DocRoot = *dec.DocRoot + } + if dec.PowFake != nil { + c.PowFake = *dec.PowFake + } + if dec.PowTest != nil { + c.PowTest = *dec.PowTest + } + if dec.PowShared != nil { + c.PowShared = *dec.PowShared + } + return nil +} diff --git a/eth/handler.go b/eth/handler.go index ade8f7719..fb8a0fd57 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -27,6 +27,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" @@ -37,7 +39,6 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" ) @@ -61,8 +62,8 @@ func errResp(code errCode, format string, v ...interface{}) error { type ProtocolManager struct { networkId int - fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) - synced uint32 // Flag whether we're considered synchronised (enables transaction processing) + fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) + acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing) txpool txPool blockchain *core.BlockChain @@ -91,13 +92,11 @@ type ProtocolManager struct { // wait group is used for graceful shutdowns during downloading // and processing wg sync.WaitGroup - - badBlockReportingEnabled bool } // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { +func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ networkId: networkId, @@ -114,18 +113,18 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int quitSync: make(chan struct{}), } // Figure out whether to allow fast sync or not - if fastSync && blockchain.CurrentBlock().NumberU64() > 0 { + if mode == downloader.FastSync && blockchain.CurrentBlock().NumberU64() > 0 { log.Warn("Blockchain not empty, fast sync disabled") - fastSync = false + mode = downloader.FullSync } - if fastSync { + if mode == downloader.FastSync { manager.fastSync = uint32(1) } // Initiate a sub-protocol for every implemented version we can handle manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions)) for i, version := range ProtocolVersions { // Skip protocol version if incompatible with the mode of operation - if fastSync && version < eth63 { + if mode == downloader.FastSync && version < eth63 { continue } // Compatible; initialise the sub-protocol @@ -160,39 +159,26 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int return nil, errIncompatibleConfig } // Construct the different synchronisation mechanisms - manager.downloader = downloader.New(downloader.FullSync, chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash, + manager.downloader = downloader.New(mode, chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash, blockchain.GetBlockByHash, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, - blockchain.GetTdByHash, blockchain.InsertHeaderChain, manager.insertChain, blockchain.InsertReceiptChain, blockchain.Rollback, + blockchain.GetTdByHash, blockchain.InsertHeaderChain, manager.blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback, manager.removePeer) - validator := func(block *types.Block, parent *types.Block) error { - return core.ValidateHeader(config, pow, block.Header(), parent.Header(), true, false) + validator := func(header *types.Header) error { + return engine.VerifyHeader(blockchain, header, true) } heighter := func() uint64 { return blockchain.CurrentBlock().NumberU64() } inserter := func(blocks types.Blocks) (int, error) { - atomic.StoreUint32(&manager.synced, 1) // Mark initial sync done on any fetcher import - return manager.insertChain(blocks) + atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import + return manager.blockchain.InsertChain(blocks) } manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer) - if blockchain.Genesis().Hash().Hex() == defaultGenesisHash && networkId == 1 { - log.Debug("Bad block reporting is enabled") - manager.badBlockReportingEnabled = true - } - return manager, nil } -func (pm *ProtocolManager) insertChain(blocks types.Blocks) (i int, err error) { - i, err = pm.blockchain.InsertChain(blocks) - if pm.badBlockReportingEnabled && core.IsValidationErr(err) && i < len(blocks) { - go sendBadBlockReport(blocks[i], err) - } - return i, err -} - func (pm *ProtocolManager) removePeer(id string) { // Short circuit if the peer was already removed peer := pm.peers.Peer(id) @@ -448,7 +434,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { p.forkDrop = nil // Validate the header and either drop the peer or continue - if err := core.ValidateDAOHeaderExtraData(pm.chainconfig, headers[0]); err != nil { + if err := misc.VerifyDAOHeaderExtraData(pm.chainconfig, headers[0]); err != nil { p.Log().Debug("Verified to be on the other side of the DAO fork, dropping") return err } @@ -657,7 +643,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { case msg.Code == TxMsg: // Transactions arrived, make sure we have a valid and fresh chain to handle them - if atomic.LoadUint32(&pm.synced) == 0 { + if atomic.LoadUint32(&pm.acceptTxs) == 0 { break } // Transactions can be processed, parse all of them and deliver to the pool diff --git a/eth/handler_test.go b/eth/handler_test.go index 683c8f71e..413ed2bff 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -24,6 +24,7 @@ import ( "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/state" "github.com/ethereum/go-ethereum/core/types" @@ -34,7 +35,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) var bigTxGas = new(big.Int).SetUint64(params.TxGas) @@ -44,11 +44,11 @@ func TestProtocolCompatibility(t *testing.T) { // Define the compatibility chart tests := []struct { version uint - fastSync bool + mode downloader.SyncMode compatible bool }{ - {61, false, true}, {62, false, true}, {63, false, true}, - {61, true, false}, {62, true, false}, {63, true, true}, + {61, downloader.FullSync, true}, {62, downloader.FullSync, true}, {63, downloader.FullSync, true}, + {61, downloader.FastSync, false}, {62, downloader.FastSync, false}, {63, downloader.FastSync, true}, } // Make sure anything we screw up is restored backup := ProtocolVersions @@ -58,7 +58,7 @@ func TestProtocolCompatibility(t *testing.T) { for i, tt := range tests { ProtocolVersions = []uint{tt.version} - pm, err := newTestProtocolManager(tt.fastSync, 0, nil, nil) + pm, err := newTestProtocolManager(tt.mode, 0, nil, nil) if pm != nil { defer pm.Stop() } @@ -73,7 +73,7 @@ func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) } func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } func testGetBlockHeaders(t *testing.T, protocol int) { - pm := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil, nil) + pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil) peer, _ := newTestPeer("peer", protocol, pm, true) defer peer.close() @@ -232,7 +232,7 @@ func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) } func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } func testGetBlockBodies(t *testing.T, protocol int) { - pm := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil, nil) + pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil) peer, _ := newTestPeer("peer", protocol, pm, true) defer peer.close() @@ -315,12 +315,12 @@ func testGetNodeData(t *testing.T, protocol int) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) block.AddTx(tx1) block.AddTx(tx2) @@ -339,7 +339,7 @@ func testGetNodeData(t *testing.T, protocol int) { } } // Assemble the test environment - pm := newTestProtocolManagerMust(t, false, 4, generator, nil) + pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) peer, _ := newTestPeer("peer", protocol, pm, true) defer peer.close() @@ -372,7 +372,7 @@ func testGetNodeData(t *testing.T, protocol int) { for i := 0; i < len(data); i++ { statedb.Put(hashes[i].Bytes(), data[i]) } - accounts := []common.Address{testBank.Address, acc1Addr, acc2Addr} + accounts := []common.Address{testBank, acc1Addr, acc2Addr} for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), statedb) @@ -407,12 +407,12 @@ func testGetReceipt(t *testing.T, protocol int) { switch i { case 0: // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) + tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) block.AddTx(tx) case 1: // In block 2, the test bank sends some more ether to account #1. // acc1Addr passes it on to account #2. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) + tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey) tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key) block.AddTx(tx1) block.AddTx(tx2) @@ -431,7 +431,7 @@ func testGetReceipt(t *testing.T, protocol int) { } } // Assemble the test environment - pm := newTestProtocolManagerMust(t, false, 4, generator, nil) + pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) peer, _ := newTestPeer("peer", protocol, pm, true) defer peer.close() @@ -469,13 +469,14 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool // Create a DAO aware protocol manager var ( evmux = new(event.TypeMux) - pow = new(pow.FakePow) + pow = ethash.NewFaker() db, _ = ethdb.NewMemDatabase() - genesis = core.WriteGenesisBlockForTesting(db) config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} + gspec = &core.Genesis{Config: config} + genesis = gspec.MustCommit(db) blockchain, _ = core.NewBlockChain(db, config, pow, evmux, vm.Config{}) ) - pm, err := NewProtocolManager(config, false, NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db) + pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db) if err != nil { t.Fatalf("failed to start test protocol manager: %v", err) } diff --git a/eth/helper_test.go b/eth/helper_test.go index 3ad31394a..21ac3724e 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -28,44 +28,45 @@ import ( "testing" "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/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) var ( testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testBank = core.GenesisAccount{ - Address: crypto.PubkeyToAddress(testBankKey.PublicKey), - Balance: big.NewInt(1000000), - } + testBank = crypto.PubkeyToAddress(testBankKey.PublicKey) ) // newTestProtocolManager creates a new protocol manager for testing purposes, // with the given number of blocks already known, and potential notification // channels for different events. -func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) { +func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) { var ( - evmux = new(event.TypeMux) - pow = new(pow.FakePow) - db, _ = ethdb.NewMemDatabase() - genesis = core.WriteGenesisBlockForTesting(db, testBank) - chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker - blockchain, _ = core.NewBlockChain(db, chainConfig, pow, evmux, vm.Config{}) + evmux = new(event.TypeMux) + engine = ethash.NewFaker() + db, _ = ethdb.NewMemDatabase() + gspec = &core.Genesis{ + Config: params.TestChainConfig, + Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}}, + } + genesis = gspec.MustCommit(db) + blockchain, _ = core.NewBlockChain(db, gspec.Config, engine, evmux, vm.Config{}) ) - chain, _ := core.GenerateChain(chainConfig, genesis, db, blocks, generator) + chain, _ := core.GenerateChain(gspec.Config, genesis, db, blocks, generator) if _, err := blockchain.InsertChain(chain); err != nil { panic(err) } - pm, err := NewProtocolManager(chainConfig, fastSync, NetworkId, 1000, evmux, &testTxPool{added: newtx}, pow, blockchain, db) + pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, 1000, evmux, &testTxPool{added: newtx}, engine, blockchain, db) if err != nil { return nil, err } @@ -77,8 +78,8 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core // with the given number of blocks already known, and potential notification // channels for different events. In case of an error, the constructor force- // fails the test. -func newTestProtocolManagerMust(t *testing.T, fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager { - pm, err := newTestProtocolManager(fastSync, blocks, generator, newtx) +func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager { + pm, err := newTestProtocolManager(mode, blocks, generator, newtx) if err != nil { t.Fatalf("Failed to create protocol manager: %v", err) } @@ -172,7 +173,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) { msg := &statusData{ ProtocolVersion: uint32(p.version), - NetworkId: uint32(NetworkId), + NetworkId: uint32(DefaultConfig.NetworkId), TD: td, CurrentBlock: head, GenesisBlock: genesis, diff --git a/eth/protocol.go b/eth/protocol.go index 7d22b33de..40997da7a 100644 --- a/eth/protocol.go +++ b/eth/protocol.go @@ -41,10 +41,7 @@ var ProtocolVersions = []uint{eth63, eth62} // Number of implemented message corresponding to different protocol versions. var ProtocolLengths = []uint64{17, 8} -const ( - NetworkId = 1 - ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message -) +const ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message // eth protocol message codes const ( diff --git a/eth/protocol_test.go b/eth/protocol_test.go index 253dcd8a7..74180bedd 100644 --- a/eth/protocol_test.go +++ b/eth/protocol_test.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" ) @@ -40,7 +41,7 @@ func TestStatusMsgErrors62(t *testing.T) { testStatusMsgErrors(t, 62) } func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) } func testStatusMsgErrors(t *testing.T, protocol int) { - pm := newTestProtocolManagerMust(t, false, 0, nil, nil) + pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) td, currentBlock, genesis := pm.blockchain.Status() defer pm.Stop() @@ -54,7 +55,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) { wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), }, { - code: StatusMsg, data: statusData{10, NetworkId, td, currentBlock, genesis}, + code: StatusMsg, data: statusData{10, uint32(DefaultConfig.NetworkId), td, currentBlock, genesis}, wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol), }, { @@ -62,7 +63,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) { wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"), }, { - code: StatusMsg, data: statusData{uint32(protocol), NetworkId, td, currentBlock, common.Hash{3}}, + code: StatusMsg, data: statusData{uint32(protocol), uint32(DefaultConfig.NetworkId), td, currentBlock, common.Hash{3}}, wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000 (!= %x)", genesis[:8]), }, } @@ -93,8 +94,8 @@ func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) } func testRecvTransactions(t *testing.T, protocol int) { txAdded := make(chan []*types.Transaction) - pm := newTestProtocolManagerMust(t, false, 0, nil, txAdded) - pm.synced = 1 // mark synced to accept transactions + pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, txAdded) + pm.acceptTxs = 1 // mark synced to accept transactions p, _ := newTestPeer("peer", protocol, pm, true) defer pm.Stop() defer p.close() @@ -120,7 +121,7 @@ func TestSendTransactions62(t *testing.T) { testSendTransactions(t, 62) } func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) } func testSendTransactions(t *testing.T, protocol int) { - pm := newTestProtocolManagerMust(t, false, 0, nil, nil) + pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) defer pm.Stop() // Fill the pool with big transactions. diff --git a/eth/sync.go b/eth/sync.go index 6e2c7c432..b0653acf9 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -175,13 +175,29 @@ func (pm *ProtocolManager) synchronise(peer *peer) { // Otherwise try to sync with the downloader mode := downloader.FullSync if atomic.LoadUint32(&pm.fastSync) == 1 { + // Fast sync was explicitly requested, and explicitly granted + mode = downloader.FastSync + } else if currentBlock.NumberU64() == 0 && pm.blockchain.CurrentFastBlock().NumberU64() > 0 { + // 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 only scenario where this can happen is if the user manually (or via a + // bad block) rolled back a fast sync node below the sync point. In this case + // however it's safe to reenable fast sync. mode = downloader.FastSync } if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil { return } - atomic.StoreUint32(&pm.synced, 1) // Mark initial sync done - + atomic.StoreUint32(&pm.acceptTxs, 1) // Mark initial sync done + if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 { + // We've completed a sync cycle, notify all peers of new state. This path is + // essential in star-topology networks where a gateway node needs to notify + // all its out-of-date peers of the availability of a new block. This failure + // scenario will most often crop up in private and hackathon networks with + // degenerate connectivity, but it should be healthy for the mainnet too to + // more reliably update peers or the local TD state. + go pm.BroadcastBlock(head, false) + } // If fast sync was enabled, and we synced up, disable it if atomic.LoadUint32(&pm.fastSync) == 1 { // Disable fast sync if we indeed have something in our chain diff --git a/eth/sync_test.go b/eth/sync_test.go index 198ffaf27..9eaa1156f 100644 --- a/eth/sync_test.go +++ b/eth/sync_test.go @@ -21,6 +21,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" ) @@ -29,12 +30,12 @@ import ( // imported into the blockchain. func TestFastSyncDisabling(t *testing.T) { // Create a pristine protocol manager, check that fast sync is left enabled - pmEmpty := newTestProtocolManagerMust(t, true, 0, nil, nil) + pmEmpty := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil) if atomic.LoadUint32(&pmEmpty.fastSync) == 0 { t.Fatalf("fast sync disabled on pristine blockchain") } // Create a full protocol manager, check that fast sync gets disabled - pmFull := newTestProtocolManagerMust(t, true, 1024, nil, nil) + pmFull := newTestProtocolManagerMust(t, downloader.FastSync, 1024, nil, nil) if atomic.LoadUint32(&pmFull.fastSync) == 1 { t.Fatalf("fast sync not disabled on non-empty blockchain") } diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 1d04d9e03..59f60d659 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -18,6 +18,7 @@ package ethclient import ( + "context" "encoding/json" "fmt" "math/big" @@ -28,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) // Client defines typed wrappers for the Ethereum RPC API. diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 7b3a3439e..c16163ace 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "math/big" + "net" "regexp" "runtime" "strconv" @@ -29,6 +30,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" @@ -53,6 +55,7 @@ type Service struct { server *p2p.Server // Peer-to-peer server to retrieve networking infos eth *eth.Ethereum // Full Ethereum service if monitoring a full node les *les.LightEthereum // Light Ethereum service if monitoring a light node + engine consensus.Engine // Consensus engine to retrieve variadic block fields node string // Name of the node to display on the monitoring page pass string // Password to authorize access to the monitoring page @@ -71,9 +74,16 @@ func New(url string, ethServ *eth.Ethereum, lesServ *les.LightEthereum) (*Servic return nil, fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url) } // Assemble and return the stats service + var engine consensus.Engine + if ethServ != nil { + engine = ethServ.Engine() + } else { + engine = lesServ.Engine() + } return &Service{ eth: ethServ, les: lesServ, + engine: engine, node: parts[1], pass: parts[3], host: parts[4], @@ -123,50 +133,59 @@ func (s *Service) loop() { // Loop reporting until termination for { - // Establish a websocket connection to the server and authenticate the node - url := fmt.Sprintf("%s/api", s.host) - if !strings.Contains(url, "://") { - url = "wss://" + url + // Resolve the URL, defaulting to TLS, but falling back to none too + path := fmt.Sprintf("%s/api", s.host) + urls := []string{path} + + if !strings.Contains(path, "://") { // url.Parse and url.IsAbs is unsuitable (https://github.com/golang/go/issues/19779) + urls = []string{"wss://" + path, "ws://" + path} + } + // Establish a websocket connection to the server on any supported URL + var ( + conf *websocket.Config + conn *websocket.Conn + err error + ) + for _, url := range urls { + if conf, err = websocket.NewConfig(url, "http://localhost/"); err != nil { + continue + } + conf.Dialer = &net.Dialer{Timeout: 3 * time.Second} + if conn, err = websocket.DialConfig(conf); err == nil { + break + } } - conn, err := websocket.Dial(url, "", "http://localhost/") if err != nil { log.Warn("Stats server unreachable", "err", err) time.Sleep(10 * time.Second) continue } - in := json.NewDecoder(conn) - out := json.NewEncoder(conn) - - if err = s.login(in, out); err != nil { + // Authenticate the client with the server + if err = s.login(conn); err != nil { log.Warn("Stats login failed", "err", err) conn.Close() time.Sleep(10 * time.Second) continue } - go s.readLoop(conn, in) + go s.readLoop(conn) // Send the initial stats so our node looks decent from the get go - if err = s.report(out); err != nil { + if err = s.report(conn); err != nil { log.Warn("Initial stats report failed", "err", err) conn.Close() continue } - if err = s.reportHistory(out, nil); err != nil { - log.Warn("Initial history report failed", "err", err) - conn.Close() - continue - } // Keep sending status updates until the connection breaks fullReport := time.NewTicker(15 * time.Second) for err == nil { select { case <-fullReport.C: - if err = s.report(out); err != nil { + if err = s.report(conn); err != nil { log.Warn("Full stats report failed", "err", err) } case list := <-s.histCh: - if err = s.reportHistory(out, list); err != nil { + if err = s.reportHistory(conn, list); err != nil { log.Warn("Requested history report failed", "err", err) } case head, ok := <-headSub.Chan(): @@ -174,10 +193,10 @@ func (s *Service) loop() { conn.Close() return } - if err = s.reportBlock(out, head.Data.(core.ChainHeadEvent).Block); err != nil { + if err = s.reportBlock(conn, head.Data.(core.ChainHeadEvent).Block); err != nil { log.Warn("Block stats report failed", "err", err) } - if err = s.reportPending(out); err != nil { + if err = s.reportPending(conn); err != nil { log.Warn("Post-block transaction stats report failed", "err", err) } case _, ok := <-txSub.Chan(): @@ -193,7 +212,7 @@ func (s *Service) loop() { exhausted = true } } - if err = s.reportPending(out); err != nil { + if err = s.reportPending(conn); err != nil { log.Warn("Transaction stats report failed", "err", err) } } @@ -207,17 +226,18 @@ func (s *Service) loop() { // from the network socket. If any of them match an active request, it forwards // it, if they themselves are requests it initiates a reply, and lastly it drops // unknown packets. -func (s *Service) readLoop(conn *websocket.Conn, in *json.Decoder) { +func (s *Service) readLoop(conn *websocket.Conn) { // If the read loop exists, close the connection defer conn.Close() for { // Retrieve the next generic network packet and bail out on error var msg map[string][]interface{} - if err := in.Decode(&msg); err != nil { + if err := websocket.JSON.Receive(conn, &msg); err != nil { log.Warn("Failed to decode stats server message", "err", err) return } + log.Trace("Received message from stats server", "msg", msg) if len(msg["emit"]) == 0 { log.Warn("Stats server sent non-broadcast", "msg", msg) return @@ -244,12 +264,13 @@ func (s *Service) readLoop(conn *websocket.Conn, in *json.Decoder) { // Make sure the request is valid and doesn't crash us request, ok := msg["emit"][1].(map[string]interface{}) if !ok { - log.Warn("Invalid history request", "msg", msg["emit"][1]) - return + log.Warn("Invalid stats history request", "msg", msg["emit"][1]) + s.histCh <- nil + continue // Ethstats sometime sends invalid history requests, ignore those } list, ok := request["list"].([]interface{}) if !ok { - log.Warn("Invalid history block list", "list", request["list"]) + log.Warn("Invalid stats history block list", "list", request["list"]) return } // Convert the block number list to an integer list @@ -257,7 +278,7 @@ func (s *Service) readLoop(conn *websocket.Conn, in *json.Decoder) { for i, num := range list { n, ok := num.(float64) if !ok { - log.Warn("Invalid history block number", "number", num) + log.Warn("Invalid stats history block number", "number", num) return } numbers[i] = uint64(n) @@ -296,7 +317,7 @@ type authMsg struct { } // login tries to authorize the client at the remote server. -func (s *Service) login(in *json.Decoder, out *json.Encoder) error { +func (s *Service) login(conn *websocket.Conn) error { // Construct and send the login authentication infos := s.server.NodeInfo() @@ -327,12 +348,12 @@ func (s *Service) login(in *json.Decoder, out *json.Encoder) error { login := map[string][]interface{}{ "emit": {"hello", auth}, } - if err := out.Encode(login); err != nil { + if err := websocket.JSON.Send(conn, login); err != nil { return err } // Retrieve the remote ack or connection termination var ack map[string][]string - if err := in.Decode(&ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" { + if err := websocket.JSON.Receive(conn, &ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" { return errors.New("unauthorized") } return nil @@ -341,17 +362,17 @@ func (s *Service) login(in *json.Decoder, out *json.Encoder) error { // report collects all possible data to report and send it to the stats server. // This should only be used on reconnects or rarely to avoid overloading the // server. Use the individual methods for reporting subscribed events. -func (s *Service) report(out *json.Encoder) error { - if err := s.reportLatency(out); err != nil { +func (s *Service) report(conn *websocket.Conn) error { + if err := s.reportLatency(conn); err != nil { return err } - if err := s.reportBlock(out, nil); err != nil { + if err := s.reportBlock(conn, nil); err != nil { return err } - if err := s.reportPending(out); err != nil { + if err := s.reportPending(conn); err != nil { return err } - if err := s.reportStats(out); err != nil { + if err := s.reportStats(conn); err != nil { return err } return nil @@ -359,7 +380,7 @@ func (s *Service) report(out *json.Encoder) error { // reportLatency sends a ping request to the server, measures the RTT time and // finally sends a latency update. -func (s *Service) reportLatency(out *json.Encoder) error { +func (s *Service) reportLatency(conn *websocket.Conn) error { // Send the current time to the ethstats server start := time.Now() @@ -369,7 +390,7 @@ func (s *Service) reportLatency(out *json.Encoder) error { "clientTime": start.String(), }}, } - if err := out.Encode(ping); err != nil { + if err := websocket.JSON.Send(conn, ping); err != nil { return err } // Wait for the pong request to arrive back @@ -380,28 +401,35 @@ func (s *Service) reportLatency(out *json.Encoder) error { // Ping timeout, abort return errors.New("ping timed out") } + latency := strconv.Itoa(int((time.Since(start) / time.Duration(2)).Nanoseconds() / 1000000)) + // Send back the measured latency - latency := map[string][]interface{}{ + log.Trace("Sending measured latency to ethstats", "latency", latency) + + stats := map[string][]interface{}{ "emit": {"latency", map[string]string{ "id": s.node, - "latency": strconv.Itoa(int((time.Since(start) / time.Duration(2)).Nanoseconds() / 1000000)), + "latency": latency, }}, } - return out.Encode(latency) + return websocket.JSON.Send(conn, stats) } // blockStats is the information to report about individual blocks. type blockStats struct { - Number *big.Int `json:"number"` - Hash common.Hash `json:"hash"` - Timestamp *big.Int `json:"timestamp"` - Miner common.Address `json:"miner"` - GasUsed *big.Int `json:"gasUsed"` - GasLimit *big.Int `json:"gasLimit"` - Diff string `json:"difficulty"` - TotalDiff string `json:"totalDifficulty"` - Txs txStats `json:"transactions"` - Uncles uncleStats `json:"uncles"` + Number *big.Int `json:"number"` + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` + Timestamp *big.Int `json:"timestamp"` + Miner common.Address `json:"miner"` + GasUsed *big.Int `json:"gasUsed"` + GasLimit *big.Int `json:"gasLimit"` + Diff string `json:"difficulty"` + TotalDiff string `json:"totalDifficulty"` + Txs txStats `json:"transactions"` + TxHash common.Hash `json:"transactionsRoot"` + Root common.Hash `json:"stateRoot"` + Uncles uncleStats `json:"uncles"` } // txStats is a custom wrapper around a transaction array to force serializing @@ -427,16 +455,21 @@ func (s uncleStats) MarshalJSON() ([]byte, error) { } // reportBlock retrieves the current chain head and repors it to the stats server. -func (s *Service) reportBlock(out *json.Encoder, block *types.Block) error { - // Assemble the block stats report and send it to the server +func (s *Service) reportBlock(conn *websocket.Conn, block *types.Block) error { + // Gather the block details from the header or block chain + details := s.assembleBlockStats(block) + + // Assemble the block report and send it to the server + log.Trace("Sending new block to ethstats", "number", details.Number, "hash", details.Hash) + stats := map[string]interface{}{ "id": s.node, - "block": s.assembleBlockStats(block), + "block": details, } report := map[string][]interface{}{ "emit": {"block", stats}, } - return out.Encode(report) + return websocket.JSON.Send(conn, report) } // assembleBlockStats retrieves any required metadata to report a single block @@ -469,23 +502,28 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats { td = s.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64()) } // Assemble and return the block stats + author, _ := s.engine.Author(header) + return &blockStats{ - Number: header.Number, - Hash: header.Hash(), - Timestamp: header.Time, - Miner: header.Coinbase, - GasUsed: new(big.Int).Set(header.GasUsed), - GasLimit: new(big.Int).Set(header.GasLimit), - Diff: header.Difficulty.String(), - TotalDiff: td.String(), - Txs: txs, - Uncles: uncles, + Number: header.Number, + Hash: header.Hash(), + ParentHash: header.ParentHash, + Timestamp: header.Time, + Miner: author, + GasUsed: new(big.Int).Set(header.GasUsed), + GasLimit: new(big.Int).Set(header.GasLimit), + Diff: header.Difficulty.String(), + TotalDiff: td.String(), + Txs: txs, + TxHash: header.TxHash, + Root: header.Root, + Uncles: uncles, } } // reportHistory retrieves the most recent batch of blocks and reports it to the // stats server. -func (s *Service) reportHistory(out *json.Encoder, list []uint64) error { +func (s *Service) reportHistory(conn *websocket.Conn, list []uint64) error { // Figure out the indexes that need reporting indexes := make([]uint64, 0, historyUpdateRange) if len(list) > 0 { @@ -493,30 +531,46 @@ func (s *Service) reportHistory(out *json.Encoder, list []uint64) error { indexes = append(indexes, list...) } else { // No indexes requested, send back the top ones - var head *types.Header + var head int64 if s.eth != nil { - head = s.eth.BlockChain().CurrentHeader() + head = s.eth.BlockChain().CurrentHeader().Number.Int64() } else { - head = s.les.BlockChain().CurrentHeader() + head = s.les.BlockChain().CurrentHeader().Number.Int64() } - start := head.Number.Int64() - historyUpdateRange + start := head - historyUpdateRange + 1 if start < 0 { start = 0 } - for i := uint64(start); i <= head.Number.Uint64(); i++ { + for i := uint64(start); i <= uint64(head); i++ { indexes = append(indexes, i) } } // Gather the batch of blocks to report history := make([]*blockStats, len(indexes)) for i, number := range indexes { + // Retrieve the next block if it's known to us + var block *types.Block if s.eth != nil { - history[i] = s.assembleBlockStats(s.eth.BlockChain().GetBlockByNumber(number)) + block = s.eth.BlockChain().GetBlockByNumber(number) } else { - history[i] = s.assembleBlockStats(types.NewBlockWithHeader(s.les.BlockChain().GetHeaderByNumber(number))) + if header := s.les.BlockChain().GetHeaderByNumber(number); header != nil { + block = types.NewBlockWithHeader(header) + } + } + // If we do have the block, add to the history and continue + if block != nil { + history[len(history)-1-i] = s.assembleBlockStats(block) + continue } + // Ran out of blocks, cut the report short and send + history = history[len(history)-i:] } // Assemble the history report and send it to the server + if len(history) > 0 { + log.Trace("Sending historical blocks to ethstats", "first", history[0].Number, "last", history[len(history)-1].Number) + } else { + log.Trace("No history to send to stats server") + } stats := map[string]interface{}{ "id": s.node, "history": history, @@ -524,7 +578,7 @@ func (s *Service) reportHistory(out *json.Encoder, list []uint64) error { report := map[string][]interface{}{ "emit": {"history", stats}, } - return out.Encode(report) + return websocket.JSON.Send(conn, report) } // pendStats is the information to report about pending transactions. @@ -534,7 +588,7 @@ type pendStats struct { // reportPending retrieves the current number of pending transactions and reports // it to the stats server. -func (s *Service) reportPending(out *json.Encoder) error { +func (s *Service) reportPending(conn *websocket.Conn) error { // Retrieve the pending count from the local blockchain var pending int if s.eth != nil { @@ -543,6 +597,8 @@ func (s *Service) reportPending(out *json.Encoder) error { pending = s.les.TxPool().Stats() } // Assemble the transaction stats and send it to the server + log.Trace("Sending pending transactions to ethstats", "count", pending) + stats := map[string]interface{}{ "id": s.node, "stats": &pendStats{ @@ -552,10 +608,10 @@ func (s *Service) reportPending(out *json.Encoder) error { report := map[string][]interface{}{ "emit": {"pending", stats}, } - return out.Encode(report) + return websocket.JSON.Send(conn, report) } -// blockStats is the information to report about the local node. +// nodeStats is the information to report about the local node. type nodeStats struct { Active bool `json:"active"` Syncing bool `json:"syncing"` @@ -568,7 +624,7 @@ type nodeStats struct { // reportPending retrieves various stats about the node at the networking and // mining layer and reports it to the stats server. -func (s *Service) reportStats(out *json.Encoder) error { +func (s *Service) reportStats(conn *websocket.Conn) error { // Gather the syncing and mining infos from the local miner instance var ( mining bool @@ -588,6 +644,9 @@ func (s *Service) reportStats(out *json.Encoder) error { sync := s.les.Downloader().Progress() syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock } + // Assemble the node stats and send it to the server + log.Trace("Sending node details to ethstats") + stats := map[string]interface{}{ "id": s.node, "stats": &nodeStats{ @@ -603,5 +662,5 @@ func (s *Service) reportStats(out *json.Encoder) error { report := map[string][]interface{}{ "emit": {"stats", stats}, } - return out.Encode(report) + return websocket.JSON.Send(conn, report) } diff --git a/event/subscription.go b/event/subscription.go index 83bd21213..02d7b9d7d 100644 --- a/event/subscription.go +++ b/event/subscription.go @@ -17,11 +17,11 @@ package event import ( + "context" "sync" "time" "github.com/ethereum/go-ethereum/common/mclock" - "golang.org/x/net/context" ) // Subscription represents a stream of events. The carrier of the events is typically a diff --git a/event/subscription_test.go b/event/subscription_test.go index a4fe30298..aa6d98984 100644 --- a/event/subscription_test.go +++ b/event/subscription_test.go @@ -17,11 +17,10 @@ package event import ( + "context" "errors" "testing" "time" - - "golang.org/x/net/context" ) var errInts = errors.New("error in subscribeInts") diff --git a/interfaces.go b/interfaces.go index f7e71a317..744f07b95 100644 --- a/interfaces.go +++ b/interfaces.go @@ -18,12 +18,12 @@ package ethereum import ( + "context" "errors" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "golang.org/x/net/context" ) // NotFound is returned by API methods if the requested item does not exist. diff --git a/internal/build/azure.go b/internal/build/azure.go index 32f535558..2081a9a0b 100644 --- a/internal/build/azure.go +++ b/internal/build/azure.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - "github.com/Azure/azure-sdk-for-go/storage" + storage "github.com/Azure/azure-storage-go" ) // AzureBlobstoreConfig is an authentication and configuration struct containing @@ -42,7 +42,6 @@ func AzureBlobstoreUpload(path string, name string, config AzureBlobstoreConfig) fmt.Printf("would upload %q to %s/%s/%s\n", path, config.Account, config.Container, name) return nil } - // Create an authenticated client against the Azure cloud rawClient, err := storage.NewBasicClient(config.Account, config.Token) if err != nil { @@ -63,3 +62,50 @@ func AzureBlobstoreUpload(path string, name string, config AzureBlobstoreConfig) } return client.CreateBlockBlobFromReader(config.Container, name, uint64(info.Size()), in, nil) } + +// AzureBlobstoreList lists all the files contained within an azure blobstore. +func AzureBlobstoreList(config AzureBlobstoreConfig) ([]storage.Blob, error) { + // Create an authenticated client against the Azure cloud + rawClient, err := storage.NewBasicClient(config.Account, config.Token) + if err != nil { + return nil, err + } + client := rawClient.GetBlobService() + + // List all the blobs from the container and return them + container := client.GetContainerReference(config.Container) + + blobs, err := container.ListBlobs(storage.ListBlobsParameters{ + MaxResults: 1024 * 1024 * 1024, // Yes, fetch all of them + Timeout: 3600, // Yes, wait for all of them + }) + if err != nil { + return nil, err + } + return blobs.Blobs, nil +} + +// AzureBlobstoreDelete iterates over a list of files to delete and removes them +// from the blobstore. +func AzureBlobstoreDelete(config AzureBlobstoreConfig, blobs []storage.Blob) error { + if *DryRunFlag { + for _, blob := range blobs { + fmt.Printf("would delete %s (%s) from %s/%s\n", blob.Name, blob.Properties.LastModified, config.Account, config.Container) + } + return nil + } + // Create an authenticated client against the Azure cloud + rawClient, err := storage.NewBasicClient(config.Account, config.Token) + if err != nil { + return err + } + client := rawClient.GetBlobService() + + // Iterate over the blobs and delete them + for _, blob := range blobs { + if err := client.DeleteBlob(config.Container, blob.Name, nil); err != nil { + return err + } + } + return nil +} diff --git a/internal/build/env.go b/internal/build/env.go index 15b2dfe41..c47681ebe 100644 --- a/internal/build/env.go +++ b/internal/build/env.go @@ -30,6 +30,7 @@ var ( GitTagFlag = flag.String("git-tag", "", `Overrides git tag being built`) BuildnumFlag = flag.String("buildnum", "", `Overrides CI build number`) PullRequestFlag = flag.Bool("pull-request", false, `Overrides pull request status of the build`) + CronJobFlag = flag.Bool("cron-job", false, `Overrides cron job status of the build`) ) // Environment contains metadata provided by the build environment. @@ -39,6 +40,7 @@ type Environment struct { Commit, Branch, Tag string // Git info Buildnum string IsPullRequest bool + IsCronJob bool } func (env Environment) String() string { @@ -59,6 +61,7 @@ func Env() Environment { Tag: os.Getenv("TRAVIS_TAG"), Buildnum: os.Getenv("TRAVIS_BUILD_NUMBER"), IsPullRequest: os.Getenv("TRAVIS_PULL_REQUEST") != "false", + IsCronJob: os.Getenv("TRAVIS_EVENT_TYPE") == "cron", } case os.Getenv("CI") == "True" && os.Getenv("APPVEYOR") == "True": return Environment{ @@ -69,6 +72,7 @@ func Env() Environment { Tag: os.Getenv("APPVEYOR_REPO_TAG_NAME"), Buildnum: os.Getenv("APPVEYOR_BUILD_NUMBER"), IsPullRequest: os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER") != "", + IsCronJob: os.Getenv("APPVEYOR_SCHEDULED_BUILD") == "True", } default: return LocalEnv() @@ -118,5 +122,8 @@ func applyEnvFlags(env Environment) Environment { if *PullRequestFlag { env.IsPullRequest = true } + if *CronJobFlag { + env.IsCronJob = true + } return env } diff --git a/internal/build/util.go b/internal/build/util.go index 1523a067b..44f6760b9 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -26,6 +26,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "text/template" ) @@ -52,19 +53,10 @@ func MustRunCommand(cmd string, args ...string) { // GOPATH returns the value that the GOPATH environment // variable should be set to. func GOPATH() string { - path := filepath.SplitList(os.Getenv("GOPATH")) - if len(path) == 0 { + if os.Getenv("GOPATH") == "" { log.Fatal("GOPATH is not set") } - // Ensure that our internal vendor folder is on GOPATH - vendor, _ := filepath.Abs(filepath.Join("build", "_vendor")) - for _, dir := range path { - if dir == vendor { - return strings.Join(path, string(filepath.ListSeparator)) - } - } - newpath := append(path[:1], append([]string{vendor}, path[1:]...)...) - return strings.Join(newpath, string(filepath.ListSeparator)) + return os.Getenv("GOPATH") } // VERSION returns the content of the VERSION file. @@ -145,3 +137,30 @@ func CopyFile(dst, src string, mode os.FileMode) { log.Fatal(err) } } + +// ExpandPackagesNoVendor expands a cmd/go import path pattern, skipping +// vendored packages. +func ExpandPackagesNoVendor(patterns []string) []string { + expand := false + for _, pkg := range patterns { + if strings.Contains(pkg, "...") { + expand = true + } + } + if expand { + args := append([]string{"list"}, patterns...) + cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...) + out, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("package listing failed: %v\n%s", err, string(out)) + } + var packages []string + for _, line := range strings.Split(string(out), "\n") { + if !strings.Contains(line, "/vendor/") { + packages = append(packages, strings.TrimSpace(line)) + } + } + return packages + } + return patterns +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c8374fe18..987e14419 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -18,6 +18,7 @@ package ethapi import ( "bytes" + "context" "encoding/hex" "errors" "fmt" @@ -30,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "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/core/vm" @@ -38,12 +40,10 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/util" - "golang.org/x/net/context" ) const ( @@ -695,8 +695,8 @@ type ExecutionResult struct { type StructLogRes struct { Pc uint64 `json:"pc"` Op string `json:"op"` - Gas *big.Int `json:"gas"` - GasCost *big.Int `json:"gasCost"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` Depth int `json:"depth"` Error error `json:"error"` Stack []string `json:"stack"` @@ -1378,7 +1378,7 @@ func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, if block == nil { return "", fmt.Errorf("block #%d not found", number) } - return fmt.Sprintf("0x%x", pow.EthashSeedHash(number)), nil + return fmt.Sprintf("0x%x", ethash.SeedHash(number)), nil } // PrivateDebugAPI is the collection of Etheruem APIs exposed over the private diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index e10fb14ff..42bf26613 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -18,6 +18,7 @@ package ethapi import ( + "context" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -30,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) // Backend interface provides the common API services (that are provided by @@ -72,7 +72,7 @@ type State interface { GetNonce(ctx context.Context, addr common.Address) (uint64, error) } -func GetAPIs(apiBackend Backend, solcPath string) []rpc.API { +func GetAPIs(apiBackend Backend) []rpc.API { return []rpc.API{ { Namespace: "eth", diff --git a/internal/ethapi/tracer.go b/internal/ethapi/tracer.go index ef107fc42..d34363564 100644 --- a/internal/ethapi/tracer.go +++ b/internal/ethapi/tracer.go @@ -23,6 +23,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/vm" "github.com/robertkrimen/otto" ) @@ -164,20 +165,53 @@ func (dw *dbWrapper) toValue(vm *otto.Otto) otto.Value { return value } +// contractWrapper provides a JS wrapper around vm.Contract +type contractWrapper struct { + contract *vm.Contract +} + +func (c *contractWrapper) caller() common.Address { + return c.contract.Caller() +} + +func (c *contractWrapper) address() common.Address { + return c.contract.Address() +} + +func (c *contractWrapper) value() *big.Int { + return c.contract.Value() +} + +func (c *contractWrapper) calldata() []byte { + return c.contract.Input +} + +func (c *contractWrapper) toValue(vm *otto.Otto) otto.Value { + value, _ := vm.ToValue(c) + obj := value.Object() + obj.Set("caller", c.caller) + obj.Set("address", c.address) + obj.Set("value", c.value) + obj.Set("calldata", c.calldata) + return value +} + // JavascriptTracer provides an implementation of Tracer that evaluates a // Javascript function for each VM execution step. type JavascriptTracer struct { - vm *otto.Otto // Javascript VM instance - traceobj *otto.Object // User-supplied object to call - log map[string]interface{} // (Reusable) map for the `log` arg to `step` - logvalue otto.Value // JS view of `log` - memory *memoryWrapper // Wrapper around the VM memory - memvalue otto.Value // JS view of `memory` - stack *stackWrapper // Wrapper around the VM stack - stackvalue otto.Value // JS view of `stack` - db *dbWrapper // Wrapper around the VM environment - dbvalue otto.Value // JS view of `db` - err error // Error, if one has occurred + vm *otto.Otto // Javascript VM instance + traceobj *otto.Object // User-supplied object to call + log map[string]interface{} // (Reusable) map for the `log` arg to `step` + logvalue otto.Value // JS view of `log` + memory *memoryWrapper // Wrapper around the VM memory + memvalue otto.Value // JS view of `memory` + stack *stackWrapper // Wrapper around the VM stack + stackvalue otto.Value // JS view of `stack` + db *dbWrapper // Wrapper around the VM environment + dbvalue otto.Value // JS view of `db` + contract *contractWrapper // Wrapper around the contract object + contractvalue otto.Value // JS view of `contract` + err error // Error, if one has occurred } // NewJavascriptTracer instantiates a new JavascriptTracer instance. @@ -189,6 +223,7 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) { // Set up builtins for this environment vm.Set("big", &fakeBig{}) + vm.Set("toHex", hexutil.Encode) jstracer, err := vm.Object("(" + code + ")") if err != nil { @@ -220,19 +255,22 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) { mem := &memoryWrapper{} stack := &stackWrapper{} db := &dbWrapper{} + contract := &contractWrapper{} return &JavascriptTracer{ - vm: vm, - traceobj: jstracer, - log: log, - logvalue: logvalue, - memory: mem, - memvalue: mem.toValue(vm), - stack: stack, - stackvalue: stack.toValue(vm), - db: db, - dbvalue: db.toValue(vm), - err: nil, + vm: vm, + traceobj: jstracer, + log: log, + logvalue: logvalue, + memory: mem, + memvalue: mem.toValue(vm), + stack: stack, + stackvalue: stack.toValue(vm), + db: db, + dbvalue: db.toValue(vm), + contract: contract, + contractvalue: contract.toValue(vm), + err: nil, }, nil } @@ -278,20 +316,22 @@ func wrapError(context string, err error) error { } // CaptureState implements the Tracer interface to trace a single step of VM execution -func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost *big.Int, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { +func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { if jst.err == nil { jst.memory.memory = memory jst.stack.stack = stack jst.db.db = env.StateDB + jst.contract.contract = contract ocw := &opCodeWrapper{op} jst.log["pc"] = pc jst.log["op"] = ocw.toValue(jst.vm) - jst.log["gas"] = gas.Int64() - jst.log["gasPrice"] = cost.Int64() + jst.log["gas"] = gas + jst.log["gasPrice"] = cost jst.log["memory"] = jst.memvalue jst.log["stack"] = jst.stackvalue + jst.log["contract"] = jst.contractvalue jst.log["depth"] = depth jst.log["account"] = contract.Address() jst.log["err"] = err diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go index 693afe802..0ef450ce3 100644 --- a/internal/ethapi/tracer_test.go +++ b/internal/ethapi/tracer_test.go @@ -136,10 +136,10 @@ func TestHaltBetweenSteps(t *testing.T) { env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0) - tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil) + tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil) timeout := errors.New("stahp") tracer.Stop(timeout) - tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil) + tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil) if _, err := tracer.GetResult(); err.Error() != "stahp in server-side tracer function 'step'" { t.Errorf("Expected timeout error, got %v", err) diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go index 5f6a2b873..782d4df80 100644 --- a/internal/jsre/deps/bindata.go +++ b/internal/jsre/deps/bindata.go @@ -84,7 +84,7 @@ func bignumberJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "bignumber.js", size: 17314, mode: os.FileMode(420), modTime: time.Unix(1484232218, 0)} + info := bindataFileInfo{name: "bignumber.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -104,7 +104,7 @@ func web3Js() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "web3.js", size: 491740, mode: os.FileMode(420), modTime: time.Unix(1484232456, 0)} + info := bindataFileInfo{name: "web3.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/internal/jsre/deps/deps.go b/internal/jsre/deps/deps.go index 8d0e1a400..fe2e6f2fa 100644 --- a/internal/jsre/deps/deps.go +++ b/internal/jsre/deps/deps.go @@ -17,4 +17,5 @@ // Package deps contains the console JavaScript dependencies Go embedded. package deps -//go:generate go-bindata -o bindata.go bignumber.js web3.js +//go:generate go-bindata -nometadata -pkg deps -o bindata.go bignumber.js web3.js +//go:generate gofmt -w -s bindata.go diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 23112c1f1..72c2bd996 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -20,6 +20,7 @@ package web3ext var Modules = map[string]string{ "admin": Admin_JS, "chequebook": Chequebook_JS, + "clique": Clique_JS, "debug": Debug_JS, "eth": Eth_JS, "miner": Miner_JS, @@ -27,6 +28,7 @@ var Modules = map[string]string{ "personal": Personal_JS, "rpc": RPC_JS, "shh": Shh_JS, + "swarmfs": SWARMFS_JS, "txpool": TxPool_JS, } @@ -62,6 +64,54 @@ web3._extend({ }); ` +const Clique_JS = ` +web3._extend({ + property: 'clique', + methods: + [ + new web3._extend.Method({ + name: 'getSnapshot', + call: 'clique_getSnapshot', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'getSnapshotAtHash', + call: 'clique_getSnapshotAtHash', + params: 1 + }), + new web3._extend.Method({ + name: 'getSigners', + call: 'clique_getSigners', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'getSignersAtHash', + call: 'clique_getSignersAtHash', + params: 1 + }), + new web3._extend.Method({ + name: 'propose', + call: 'clique_propose', + params: 2 + }), + new web3._extend.Method({ + name: 'discard', + call: 'clique_discard', + params: 1 + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'proposals', + getter: 'clique_proposals' + }), + ] +}); +` + const Admin_JS = ` web3._extend({ property: 'admin', @@ -94,11 +144,6 @@ web3._extend({ params: 2 }), new web3._extend.Method({ - name: 'setSolc', - call: 'admin_setSolc', - params: 1 - }), - new web3._extend.Method({ name: 'startRPC', call: 'admin_startRPC', params: 4, @@ -486,6 +531,29 @@ web3._extend({ ] }); ` +const SWARMFS_JS = ` +web3._extend({ + property: 'swarmfs', + methods: + [ + new web3._extend.Method({ + name: 'mount', + call: 'swarmfs_mount', + params: 2 + }), + new web3._extend.Method({ + name: 'unmount', + call: 'swarmfs_unmount', + params: 1 + }), + new web3._extend.Method({ + name: 'listmounts', + call: 'swarmfs_listmounts', + params: 0 + }) + ] +}); +` const TxPool_JS = ` web3._extend({ diff --git a/les/api_backend.go b/les/api_backend.go index 264b381f5..7d69046de 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -17,6 +17,7 @@ package les import ( + "context" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -33,12 +34,11 @@ import ( "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) type LesApiBackend struct { eth *LightEthereum - gpo *gasprice.LightPriceOracle + gpo *gasprice.Oracle } func (b *LesApiBackend) ChainConfig() *params.ChainConfig { @@ -50,6 +50,7 @@ func (b *LesApiBackend) CurrentBlock() *types.Block { } func (b *LesApiBackend) SetHead(number uint64) { + b.eth.protocolManager.downloader.Cancel() b.eth.blockchain.SetHead(number) } @@ -99,7 +100,7 @@ func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state etha from.SetBalance(math.MaxBig256) vmstate := light.NewVMState(ctx, stateDb) - context := core.NewEVMContext(msg, header, b.eth.blockchain) + context := core.NewEVMContext(msg, header, b.eth.blockchain, nil) return vm.NewEVM(context, vmstate, b.eth.chainConfig, vmCfg), vmstate.Error, nil } diff --git a/les/backend.go b/les/backend.go index 404728c0e..783e6e94e 100644 --- a/les/backend.go +++ b/les/backend.go @@ -18,14 +18,13 @@ package les import ( - "errors" "fmt" "time" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" @@ -40,7 +39,6 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" rpc "github.com/ethereum/go-ethereum/rpc" ) @@ -60,10 +58,8 @@ type LightEthereum struct { ApiBackend *LesApiBackend eventMux *event.TypeMux - pow pow.PoW + engine consensus.Engine accountManager *accounts.Manager - solcPath string - solc *compiler.Solidity netVersionId int netRPCService *ethapi.PublicNetAPI @@ -74,42 +70,49 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { if err != nil { return nil, err } - if err := eth.SetupGenesisBlock(&chainDb, config); err != nil { - return nil, err + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) + if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { + return nil, genesisErr } + log.Info("Initialised chain configuration", "config", chainConfig) + odr := NewLesOdr(chainDb) relay := NewLesTxRelay() eth := &LightEthereum{ odr: odr, relay: relay, chainDb: chainDb, + chainConfig: chainConfig, eventMux: ctx.EventMux, accountManager: ctx.AccountManager, - pow: eth.CreatePoW(ctx, config), + engine: eth.CreateConsensusEngine(ctx, config, chainConfig, chainDb), shutdownChan: make(chan bool), netVersionId: config.NetworkId, - solcPath: config.SolcPath, - } - - if config.ChainConfig == nil { - return nil, errors.New("missing chain config") } - eth.chainConfig = config.ChainConfig - eth.blockchain, err = light.NewLightChain(odr, eth.chainConfig, eth.pow, eth.eventMux) - if err != nil { - if err == core.ErrNoGenesis { - return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`) - } + if eth.blockchain, err = light.NewLightChain(odr, eth.chainConfig, eth.engine, eth.eventMux); err != nil { return nil, err } + // Rewind the chain in case of an incompatible config upgrade. + if compat, ok := genesisErr.(*params.ConfigCompatError); ok { + log.Warn("Rewinding chain to upgrade configuration", "err", compat) + eth.blockchain.SetHead(compat.RewindTo) + core.WriteChainConfig(chainDb, genesisHash, chainConfig) + } eth.txPool = light.NewTxPool(eth.chainConfig, eth.eventMux, eth.blockchain, eth.relay) - if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.LightMode, config.NetworkId, eth.eventMux, eth.pow, eth.blockchain, nil, chainDb, odr, relay); err != nil { + lightSync := config.SyncMode == downloader.LightSync + if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, lightSync, config.NetworkId, eth.eventMux, eth.engine, eth.blockchain, nil, chainDb, odr, relay); err != nil { return nil, err } + relay.ps = eth.protocolManager.peers + relay.reqDist = eth.protocolManager.reqDist eth.ApiBackend = &LesApiBackend{eth, nil} - eth.ApiBackend.gpo = gasprice.NewLightPriceOracle(eth.ApiBackend) + gpoParams := config.GPO + if gpoParams.Default == nil { + gpoParams.Default = config.GasPrice + } + eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams) return eth, nil } @@ -138,7 +141,7 @@ func (s *LightDummyAPI) Mining() bool { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *LightEthereum) APIs() []rpc.API { - return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ + return append(ethapi.GetAPIs(s.ApiBackend), []rpc.API{ { Namespace: "eth", Version: "1.0", @@ -169,6 +172,7 @@ func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) { func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain } func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } +func (s *LightEthereum) Engine() consensus.Engine { return s.engine } func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } diff --git a/les/distributor.go b/les/distributor.go new file mode 100644 index 000000000..c59b36146 --- /dev/null +++ b/les/distributor.go @@ -0,0 +1,259 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package light implements on-demand retrieval capable state and chain objects +// for the Ethereum Light Client. +package les + +import ( + "container/list" + "errors" + "sync" + "time" +) + +// ErrNoPeers is returned if no peers capable of serving a queued request are available +var ErrNoPeers = errors.New("no suitable peers available") + +// requestDistributor implements a mechanism that distributes requests to +// suitable peers, obeying flow control rules and prioritizing them in creation +// order (even when a resend is necessary). +type requestDistributor struct { + reqQueue *list.List + lastReqOrder uint64 + stopChn, loopChn chan struct{} + loopNextSent bool + lock sync.Mutex + + getAllPeers func() map[distPeer]struct{} +} + +// distPeer is an LES server peer interface for the request distributor. +// waitBefore returns either the necessary waiting time before sending a request +// with the given upper estimated cost or the estimated remaining relative buffer +// value after sending such a request (in which case the request can be sent +// immediately). At least one of these values is always zero. +type distPeer interface { + waitBefore(uint64) (time.Duration, float64) + canQueue() bool + queueSend(f func()) +} + +// distReq is the request abstraction used by the distributor. It is based on +// three callback functions: +// - getCost returns the upper estimate of the cost of sending the request to a given peer +// - canSend tells if the server peer is suitable to serve the request +// - request prepares sending the request to the given peer and returns a function that +// does the actual sending. Request order should be preserved but the callback itself should not +// block until it is sent because other peers might still be able to receive requests while +// one of them is blocking. Instead, the returned function is put in the peer's send queue. +type distReq struct { + getCost func(distPeer) uint64 + canSend func(distPeer) bool + request func(distPeer) func() + + reqOrder uint64 + sentChn chan distPeer + element *list.Element +} + +// newRequestDistributor creates a new request distributor +func newRequestDistributor(getAllPeers func() map[distPeer]struct{}, stopChn chan struct{}) *requestDistributor { + r := &requestDistributor{ + reqQueue: list.New(), + loopChn: make(chan struct{}, 2), + stopChn: stopChn, + getAllPeers: getAllPeers, + } + go r.loop() + return r +} + +// distMaxWait is the maximum waiting time after which further necessary waiting +// times are recalculated based on new feedback from the servers +const distMaxWait = time.Millisecond * 10 + +// main event loop +func (d *requestDistributor) loop() { + for { + select { + case <-d.stopChn: + d.lock.Lock() + elem := d.reqQueue.Front() + for elem != nil { + close(elem.Value.(*distReq).sentChn) + elem = elem.Next() + } + d.lock.Unlock() + return + case <-d.loopChn: + d.lock.Lock() + d.loopNextSent = false + loop: + for { + peer, req, wait := d.nextRequest() + if req != nil && wait == 0 { + chn := req.sentChn // save sentChn because remove sets it to nil + d.remove(req) + send := req.request(peer) + if send != nil { + peer.queueSend(send) + } + chn <- peer + close(chn) + } else { + if wait == 0 { + // no request to send and nothing to wait for; the next + // queued request will wake up the loop + break loop + } + d.loopNextSent = true // a "next" signal has been sent, do not send another one until this one has been received + if wait > distMaxWait { + // waiting times may be reduced by incoming request replies, if it is too long, recalculate it periodically + wait = distMaxWait + } + go func() { + time.Sleep(wait) + d.loopChn <- struct{}{} + }() + break loop + } + } + d.lock.Unlock() + } + } +} + +// selectPeerItem represents a peer to be selected for a request by weightedRandomSelect +type selectPeerItem struct { + peer distPeer + req *distReq + weight int64 +} + +// Weight implements wrsItem interface +func (sp selectPeerItem) Weight() int64 { + return sp.weight +} + +// nextRequest returns the next possible request from any peer, along with the +// associated peer and necessary waiting time +func (d *requestDistributor) nextRequest() (distPeer, *distReq, time.Duration) { + peers := d.getAllPeers() + + elem := d.reqQueue.Front() + var ( + bestPeer distPeer + bestReq *distReq + bestWait time.Duration + sel *weightedRandomSelect + ) + + for (len(peers) > 0 || elem == d.reqQueue.Front()) && elem != nil { + req := elem.Value.(*distReq) + canSend := false + for peer, _ := range peers { + if peer.canQueue() && req.canSend(peer) { + canSend = true + cost := req.getCost(peer) + wait, bufRemain := peer.waitBefore(cost) + if wait == 0 { + if sel == nil { + sel = newWeightedRandomSelect() + } + sel.update(selectPeerItem{peer: peer, req: req, weight: int64(bufRemain*1000000) + 1}) + } else { + if bestReq == nil || wait < bestWait { + bestPeer = peer + bestReq = req + bestWait = wait + } + } + delete(peers, peer) + } + } + next := elem.Next() + if !canSend && elem == d.reqQueue.Front() { + close(req.sentChn) + d.remove(req) + } + elem = next + } + + if sel != nil { + c := sel.choose().(selectPeerItem) + return c.peer, c.req, 0 + } + return bestPeer, bestReq, bestWait +} + +// queue adds a request to the distribution queue, returns a channel where the +// receiving peer is sent once the request has been sent (request callback returned). +// If the request is cancelled or timed out without suitable peers, the channel is +// closed without sending any peer references to it. +func (d *requestDistributor) queue(r *distReq) chan distPeer { + d.lock.Lock() + defer d.lock.Unlock() + + if r.reqOrder == 0 { + d.lastReqOrder++ + r.reqOrder = d.lastReqOrder + } + + back := d.reqQueue.Back() + if back == nil || r.reqOrder > back.Value.(*distReq).reqOrder { + r.element = d.reqQueue.PushBack(r) + } else { + before := d.reqQueue.Front() + for before.Value.(*distReq).reqOrder < r.reqOrder { + before = before.Next() + } + r.element = d.reqQueue.InsertBefore(r, before) + } + + if !d.loopNextSent { + d.loopNextSent = true + d.loopChn <- struct{}{} + } + + r.sentChn = make(chan distPeer, 1) + return r.sentChn +} + +// cancel removes a request from the queue if it has not been sent yet (returns +// false if it has been sent already). It is guaranteed that the callback functions +// will not be called after cancel returns. +func (d *requestDistributor) cancel(r *distReq) bool { + d.lock.Lock() + defer d.lock.Unlock() + + if r.sentChn == nil { + return false + } + + close(r.sentChn) + d.remove(r) + return true +} + +// remove removes a request from the queue +func (d *requestDistributor) remove(r *distReq) { + r.sentChn = nil + if r.element != nil { + d.reqQueue.Remove(r.element) + r.element = nil + } +} diff --git a/les/distributor_test.go b/les/distributor_test.go new file mode 100644 index 000000000..f2eb80729 --- /dev/null +++ b/les/distributor_test.go @@ -0,0 +1,192 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package light implements on-demand retrieval capable state and chain objects +// for the Ethereum Light Client. +package les + +import ( + "math/rand" + "sync" + "testing" + "time" +) + +type testDistReq struct { + cost, procTime, order uint64 + canSendTo map[*testDistPeer]struct{} +} + +func (r *testDistReq) getCost(dp distPeer) uint64 { + return r.cost +} + +func (r *testDistReq) canSend(dp distPeer) bool { + _, ok := r.canSendTo[dp.(*testDistPeer)] + return ok +} + +func (r *testDistReq) request(dp distPeer) func() { + return func() { dp.(*testDistPeer).send(r) } +} + +type testDistPeer struct { + sent []*testDistReq + sumCost uint64 + lock sync.RWMutex +} + +func (p *testDistPeer) send(r *testDistReq) { + p.lock.Lock() + defer p.lock.Unlock() + + p.sent = append(p.sent, r) + p.sumCost += r.cost +} + +func (p *testDistPeer) worker(t *testing.T, checkOrder bool, stop chan struct{}) { + var last uint64 + for { + wait := time.Millisecond + p.lock.Lock() + if len(p.sent) > 0 { + rq := p.sent[0] + wait = time.Duration(rq.procTime) + p.sumCost -= rq.cost + if checkOrder { + if rq.order <= last { + t.Errorf("Requests processed in wrong order") + } + last = rq.order + } + p.sent = p.sent[1:] + } + p.lock.Unlock() + select { + case <-stop: + return + case <-time.After(wait): + } + } +} + +const ( + testDistBufLimit = 10000000 + testDistMaxCost = 1000000 + testDistPeerCount = 5 + testDistReqCount = 50000 + testDistMaxResendCount = 3 +) + +func (p *testDistPeer) waitBefore(cost uint64) (time.Duration, float64) { + p.lock.RLock() + sumCost := p.sumCost + cost + p.lock.RUnlock() + if sumCost < testDistBufLimit { + return 0, float64(testDistBufLimit-sumCost) / float64(testDistBufLimit) + } else { + return time.Duration(sumCost - testDistBufLimit), 0 + } +} + +func (p *testDistPeer) canQueue() bool { + return true +} + +func (p *testDistPeer) queueSend(f func()) { + f() +} + +func TestRequestDistributor(t *testing.T) { + testRequestDistributor(t, false) +} + +func TestRequestDistributorResend(t *testing.T) { + testRequestDistributor(t, true) +} + +func testRequestDistributor(t *testing.T, resend bool) { + stop := make(chan struct{}) + defer close(stop) + + var peers [testDistPeerCount]*testDistPeer + for i, _ := range peers { + peers[i] = &testDistPeer{} + go peers[i].worker(t, !resend, stop) + } + + dist := newRequestDistributor(func() map[distPeer]struct{} { + m := make(map[distPeer]struct{}) + for _, peer := range peers { + m[peer] = struct{}{} + } + return m + }, stop) + + var wg sync.WaitGroup + + for i := 1; i <= testDistReqCount; i++ { + cost := uint64(rand.Int63n(testDistMaxCost)) + procTime := uint64(rand.Int63n(int64(cost + 1))) + rq := &testDistReq{ + cost: cost, + procTime: procTime, + order: uint64(i), + canSendTo: make(map[*testDistPeer]struct{}), + } + for _, peer := range peers { + if rand.Intn(2) != 0 { + rq.canSendTo[peer] = struct{}{} + } + } + + wg.Add(1) + req := &distReq{ + getCost: rq.getCost, + canSend: rq.canSend, + request: rq.request, + } + chn := dist.queue(req) + go func() { + cnt := 1 + if resend && len(rq.canSendTo) != 0 { + cnt = rand.Intn(testDistMaxResendCount) + 1 + } + for i := 0; i < cnt; i++ { + if i != 0 { + chn = dist.queue(req) + } + p := <-chn + if p == nil { + if len(rq.canSendTo) != 0 { + t.Errorf("Request that could have been sent was dropped") + } + } else { + peer := p.(*testDistPeer) + if _, ok := rq.canSendTo[peer]; !ok { + t.Errorf("Request sent to wrong peer") + } + } + } + wg.Done() + }() + if rand.Intn(1000) == 0 { + time.Sleep(time.Duration(rand.Intn(5000000))) + } + } + + wg.Wait() +} diff --git a/les/execqueue.go b/les/execqueue.go new file mode 100644 index 000000000..ac779003b --- /dev/null +++ b/les/execqueue.go @@ -0,0 +1,71 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package les + +import ( + "sync/atomic" +) + +// ExecQueue implements a queue that executes function calls in a single thread, +// in the same order as they have been queued. +type execQueue struct { + chn chan func() + cnt, stop, capacity int32 +} + +// NewExecQueue creates a new execution queue. +func newExecQueue(capacity int32) *execQueue { + q := &execQueue{ + chn: make(chan func(), capacity), + capacity: capacity, + } + go q.loop() + return q +} + +func (q *execQueue) loop() { + for f := range q.chn { + atomic.AddInt32(&q.cnt, -1) + if atomic.LoadInt32(&q.stop) != 0 { + return + } + f() + } +} + +// CanQueue returns true if more function calls can be added to the execution queue. +func (q *execQueue) canQueue() bool { + return atomic.LoadInt32(&q.stop) == 0 && atomic.LoadInt32(&q.cnt) < q.capacity +} + +// Queue adds a function call to the execution queue. Returns true if successful. +func (q *execQueue) queue(f func()) bool { + if atomic.LoadInt32(&q.stop) != 0 { + return false + } + if atomic.AddInt32(&q.cnt, 1) > q.capacity { + atomic.AddInt32(&q.cnt, -1) + return false + } + q.chn <- f + return true +} + +// Stop stops the exec queue. +func (q *execQueue) quit() { + atomic.StoreInt32(&q.stop, 1) +} diff --git a/les/fetcher.go b/les/fetcher.go index f9e517d25..a294d00d5 100644 --- a/les/fetcher.go +++ b/les/fetcher.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/light" @@ -135,35 +136,38 @@ func (f *lightFetcher) syncLoop() { f.lock.Lock() s := requesting requesting = false + var ( + rq *distReq + reqID uint64 + ) if !f.syncing && !(newAnnounce && s) { - reqID := getNextReqID() - if peer, node, amount, retry := f.nextRequest(reqID); node != nil { - requesting = true - if reqID, ok := f.request(peer, reqID, node, amount); ok { - go func() { - time.Sleep(softRequestTimeout) - f.reqMu.Lock() - req, ok := f.requested[reqID] - if ok { - req.timeout = true - f.requested[reqID] = req - } - f.reqMu.Unlock() - // keep starting new requests while possible - f.requestChn <- false - }() - } - } else { - if retry { - requesting = true - go func() { - time.Sleep(time.Millisecond * 100) - f.requestChn <- false - }() - } - } + rq, reqID = f.nextRequest() } + syncing := f.syncing f.lock.Unlock() + + if rq != nil { + requesting = true + _, ok := <-f.pm.reqDist.queue(rq) + if !ok { + f.requestChn <- false + } + + if !syncing { + go func() { + time.Sleep(softRequestTimeout) + f.reqMu.Lock() + req, ok := f.requested[reqID] + if ok { + req.timeout = true + f.requested[reqID] = req + } + f.reqMu.Unlock() + // keep starting new requests while possible + f.requestChn <- false + }() + } + } case reqID := <-f.timeoutChn: f.reqMu.Lock() req, ok := f.requested[reqID] @@ -334,6 +338,12 @@ func (f *lightFetcher) peerHasBlock(p *peer, hash common.Hash, number uint64) bo f.lock.Lock() defer f.lock.Unlock() + if f.syncing { + // always return true when syncing + // false positives are acceptable, a more sophisticated condition can be implemented later + return true + } + fp := f.peers[p] if fp == nil || fp.root == nil { return false @@ -346,43 +356,13 @@ func (f *lightFetcher) peerHasBlock(p *peer, hash common.Hash, number uint64) bo f.chain.LockChain() defer f.chain.UnlockChain() // if it's older than the peer's block tree root but it's in the same canonical chain - // than the root, we can still be sure the peer knows it + // as the root, we can still be sure the peer knows it + // + // when syncing, just check if it is part of the known chain, there is nothing better we + // can do since we do not know the most recent block hash yet return core.GetCanonicalHash(f.pm.chainDb, fp.root.number) == fp.root.hash && core.GetCanonicalHash(f.pm.chainDb, number) == hash } -// request initiates a header download request from a certain peer -func (f *lightFetcher) request(p *peer, reqID uint64, n *fetcherTreeNode, amount uint64) (uint64, bool) { - fp := f.peers[p] - if fp == nil { - p.Log().Debug("Requesting from unknown peer") - p.fcServer.DeassignRequest(reqID) - return 0, false - } - if fp.bestConfirmed == nil || fp.root == nil || !f.checkKnownNode(p, fp.root) { - f.syncing = true - go func() { - p.Log().Debug("Synchronisation started") - f.pm.synchronise(p) - f.syncDone <- p - }() - p.fcServer.DeassignRequest(reqID) - return 0, false - } - - n.requested = true - cost := p.GetRequestCost(GetBlockHeadersMsg, int(amount)) - p.fcServer.SendRequest(reqID, cost) - f.reqMu.Lock() - f.requested[reqID] = fetchRequest{hash: n.hash, amount: amount, peer: p, sent: mclock.Now()} - f.reqMu.Unlock() - go p.RequestHeadersByHash(reqID, cost, n.hash, int(amount), 0, true) - go func() { - time.Sleep(hardRequestTimeout) - f.timeoutChn <- reqID - }() - return reqID, true -} - // requestAmount calculates the amount of headers to be downloaded starting // from a certain head backwards func (f *lightFetcher) requestAmount(p *peer, n *fetcherTreeNode) uint64 { @@ -408,12 +388,13 @@ func (f *lightFetcher) requestedID(reqID uint64) bool { // nextRequest selects the peer and announced head to be requested next, amount // to be downloaded starting from the head backwards is also returned -func (f *lightFetcher) nextRequest(reqID uint64) (*peer, *fetcherTreeNode, uint64, bool) { +func (f *lightFetcher) nextRequest() (*distReq, uint64) { var ( bestHash common.Hash bestAmount uint64 ) bestTd := f.maxConfirmedTd + bestSyncing := false for p, fp := range f.peers { for hash, n := range fp.nodeByHash { @@ -423,29 +404,83 @@ func (f *lightFetcher) nextRequest(reqID uint64) (*peer, *fetcherTreeNode, uint6 bestHash = hash bestAmount = amount bestTd = n.td + bestSyncing = fp.bestConfirmed == nil || fp.root == nil || !f.checkKnownNode(p, fp.root) } } } } if bestTd == f.maxConfirmedTd { - return nil, nil, 0, false - } - - peer, _, locked := f.pm.serverPool.selectPeer(reqID, func(p *peer) (bool, time.Duration) { - fp := f.peers[p] - if fp == nil || fp.nodeByHash[bestHash] == nil { - return false, 0 + return nil, 0 + } + + f.syncing = bestSyncing + + var rq *distReq + reqID := getNextReqID() + if f.syncing { + rq = &distReq{ + getCost: func(dp distPeer) uint64 { + return 0 + }, + canSend: func(dp distPeer) bool { + p := dp.(*peer) + fp := f.peers[p] + return fp != nil && fp.nodeByHash[bestHash] != nil + }, + request: func(dp distPeer) func() { + go func() { + p := dp.(*peer) + p.Log().Debug("Synchronisation started") + f.pm.synchronise(p) + f.syncDone <- p + }() + return nil + }, + } + } else { + rq = &distReq{ + getCost: func(dp distPeer) uint64 { + p := dp.(*peer) + return p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount)) + }, + canSend: func(dp distPeer) bool { + p := dp.(*peer) + f.lock.Lock() + defer f.lock.Unlock() + + fp := f.peers[p] + if fp == nil { + return false + } + n := fp.nodeByHash[bestHash] + return n != nil && !n.requested + }, + request: func(dp distPeer) func() { + p := dp.(*peer) + f.lock.Lock() + fp := f.peers[p] + if fp != nil { + n := fp.nodeByHash[bestHash] + if n != nil { + n.requested = true + } + } + f.lock.Unlock() + + cost := p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount)) + p.fcServer.QueueRequest(reqID, cost) + f.reqMu.Lock() + f.requested[reqID] = fetchRequest{hash: bestHash, amount: bestAmount, peer: p, sent: mclock.Now()} + f.reqMu.Unlock() + go func() { + time.Sleep(hardRequestTimeout) + f.timeoutChn <- reqID + }() + return func() { p.RequestHeadersByHash(reqID, cost, bestHash, int(bestAmount), 0, true) } + }, } - return true, p.fcServer.CanSend(p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount))) - }) - if !locked { - return nil, nil, 0, true - } - var node *fetcherTreeNode - if peer != nil { - node = f.peers[peer].nodeByHash[bestHash] } - return peer, node, bestAmount, false + return rq, reqID } // deliverHeaders delivers header download request responses for processing @@ -464,7 +499,7 @@ func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) boo headers[int(req.amount)-1-i] = header } if _, err := f.chain.InsertHeaderChain(headers, 1); err != nil { - if err == core.BlockFutureErr { + if err == consensus.ErrFutureBlock { return true } log.Debug("Failed to insert header chain", "err", err) diff --git a/les/flowcontrol/control.go b/les/flowcontrol/control.go index e45537cf5..e40e69346 100644 --- a/les/flowcontrol/control.go +++ b/les/flowcontrol/control.go @@ -94,14 +94,12 @@ func (peer *ClientNode) RequestProcessed(cost uint64) (bv, realCost uint64) { } type ServerNode struct { - bufEstimate uint64 - lastTime mclock.AbsTime - params *ServerParams - sumCost uint64 // sum of req costs sent to this server - pending map[uint64]uint64 // value = sumCost after sending the given req - assignedRequest uint64 // when != 0, only the request with the given ID can be sent to this peer - assignToken chan struct{} // send to this channel before assigning, read from it after deassigning - lock sync.RWMutex + bufEstimate uint64 + lastTime mclock.AbsTime + params *ServerParams + sumCost uint64 // sum of req costs sent to this server + pending map[uint64]uint64 // value = sumCost after sending the given req + lock sync.RWMutex } func NewServerNode(params *ServerParams) *ServerNode { @@ -110,7 +108,6 @@ func NewServerNode(params *ServerParams) *ServerNode { lastTime: mclock.Now(), params: params, pending: make(map[uint64]uint64), - assignToken: make(chan struct{}, 1), } } @@ -127,94 +124,37 @@ func (peer *ServerNode) recalcBLE(time mclock.AbsTime) { } // safetyMargin is added to the flow control waiting time when estimated buffer value is low -const safetyMargin = time.Millisecond * 200 +const safetyMargin = time.Millisecond -func (peer *ServerNode) canSend(maxCost uint64) time.Duration { +func (peer *ServerNode) canSend(maxCost uint64) (time.Duration, float64) { + peer.recalcBLE(mclock.Now()) maxCost += uint64(safetyMargin) * peer.params.MinRecharge / uint64(fcTimeConst) if maxCost > peer.params.BufLimit { maxCost = peer.params.BufLimit } if peer.bufEstimate >= maxCost { - return 0 + return 0, float64(peer.bufEstimate-maxCost) / float64(peer.params.BufLimit) } - return time.Duration((maxCost - peer.bufEstimate) * uint64(fcTimeConst) / peer.params.MinRecharge) + return time.Duration((maxCost - peer.bufEstimate) * uint64(fcTimeConst) / peer.params.MinRecharge), 0 } // CanSend returns the minimum waiting time required before sending a request -// with the given maximum estimated cost -func (peer *ServerNode) CanSend(maxCost uint64) time.Duration { +// with the given maximum estimated cost. Second return value is the relative +// estimated buffer level after sending the request (divided by BufLimit). +func (peer *ServerNode) CanSend(maxCost uint64) (time.Duration, float64) { peer.lock.RLock() defer peer.lock.RUnlock() return peer.canSend(maxCost) } -// AssignRequest tries to assign the server node to the given request, guaranteeing -// that once it returns true, no request will be sent to the node before this one -func (peer *ServerNode) AssignRequest(reqID uint64) bool { - select { - case peer.assignToken <- struct{}{}: - default: - return false - } - peer.lock.Lock() - peer.assignedRequest = reqID - peer.lock.Unlock() - return true -} - -// MustAssignRequest waits until the node can be assigned to the given request. -// It is always guaranteed that assignments are released in a short amount of time. -func (peer *ServerNode) MustAssignRequest(reqID uint64) { - peer.assignToken <- struct{}{} - peer.lock.Lock() - peer.assignedRequest = reqID - peer.lock.Unlock() -} - -// DeassignRequest releases a request assignment in case the planned request -// is not being sent. -func (peer *ServerNode) DeassignRequest(reqID uint64) { - peer.lock.Lock() - if peer.assignedRequest == reqID { - peer.assignedRequest = 0 - <-peer.assignToken - } - peer.lock.Unlock() -} - -// IsAssigned returns true if the server node has already been assigned to a request -// (note that this function returning false does not guarantee that you can assign a request -// immediately afterwards, its only purpose is to help peer selection) -func (peer *ServerNode) IsAssigned() bool { - peer.lock.RLock() - locked := peer.assignedRequest != 0 - peer.lock.RUnlock() - return locked -} - -// blocks until request can be sent -func (peer *ServerNode) SendRequest(reqID, maxCost uint64) { +// QueueRequest should be called when the request has been assigned to the given +// server node, before putting it in the send queue. It is mandatory that requests +// are sent in the same order as the QueueRequest calls are made. +func (peer *ServerNode) QueueRequest(reqID, maxCost uint64) { peer.lock.Lock() defer peer.lock.Unlock() - if peer.assignedRequest != reqID { - peer.lock.Unlock() - peer.MustAssignRequest(reqID) - peer.lock.Lock() - } - - peer.recalcBLE(mclock.Now()) - wait := peer.canSend(maxCost) - for wait > 0 { - peer.lock.Unlock() - time.Sleep(wait) - peer.lock.Lock() - peer.recalcBLE(mclock.Now()) - wait = peer.canSend(maxCost) - } - peer.assignedRequest = 0 - <-peer.assignToken peer.bufEstimate -= maxCost peer.sumCost += maxCost if reqID >= 0 { @@ -222,6 +162,8 @@ func (peer *ServerNode) SendRequest(reqID, maxCost uint64) { } } +// GotReply adjusts estimated buffer value according to the value included in +// the latest request reply. func (peer *ServerNode) GotReply(reqID, bv uint64) { peer.lock.Lock() @@ -235,6 +177,10 @@ func (peer *ServerNode) GotReply(reqID, bv uint64) { return } delete(peer.pending, reqID) - peer.bufEstimate = bv - (peer.sumCost - sc) + cc := peer.sumCost - sc + peer.bufEstimate = 0 + if bv > cc { + peer.bufEstimate = bv - cc + } peer.lastTime = mclock.Now() } diff --git a/les/handler.go b/les/handler.go index 4271da8b8..fbb9e9906 100644 --- a/les/handler.go +++ b/les/handler.go @@ -27,6 +27,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -39,7 +40,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) @@ -102,6 +102,7 @@ type ProtocolManager struct { odr *LesOdr server *LesServer serverPool *serverPool + reqDist *requestDistributor downloader *downloader.Downloader fetcher *lightFetcher @@ -127,7 +128,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { +func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, engine consensus.Engine, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ lightSync: lightSync, @@ -203,8 +204,17 @@ func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, network blockchain.InsertHeaderChain, nil, nil, blockchain.Rollback, removePeer) } + manager.reqDist = newRequestDistributor(func() map[distPeer]struct{} { + m := make(map[distPeer]struct{}) + peers := manager.peers.AllPeers() + for _, peer := range peers { + m[peer] = struct{}{} + } + return m + }, manager.quitSync) if odr != nil { odr.removePeer = removePeer + odr.reqDist = manager.reqDist } /*validator := func(block *types.Block, parent *types.Block) error { @@ -334,17 +344,49 @@ func (pm *ProtocolManager) handle(p *peer) error { if pm.lightSync { requestHeadersByHash := func(origin common.Hash, amount int, skip int, reverse bool) error { reqID := getNextReqID() - cost := p.GetRequestCost(GetBlockHeadersMsg, amount) - p.fcServer.MustAssignRequest(reqID) - p.fcServer.SendRequest(reqID, cost) - return p.RequestHeadersByHash(reqID, cost, origin, amount, skip, reverse) + rq := &distReq{ + getCost: func(dp distPeer) uint64 { + peer := dp.(*peer) + return peer.GetRequestCost(GetBlockHeadersMsg, amount) + }, + canSend: func(dp distPeer) bool { + return dp.(*peer) == p + }, + request: func(dp distPeer) func() { + peer := dp.(*peer) + cost := peer.GetRequestCost(GetBlockHeadersMsg, amount) + peer.fcServer.QueueRequest(reqID, cost) + return func() { peer.RequestHeadersByHash(reqID, cost, origin, amount, skip, reverse) } + }, + } + _, ok := <-pm.reqDist.queue(rq) + if !ok { + return ErrNoPeers + } + return nil } requestHeadersByNumber := func(origin uint64, amount int, skip int, reverse bool) error { reqID := getNextReqID() - cost := p.GetRequestCost(GetBlockHeadersMsg, amount) - p.fcServer.MustAssignRequest(reqID) - p.fcServer.SendRequest(reqID, cost) - return p.RequestHeadersByNumber(reqID, cost, origin, amount, skip, reverse) + rq := &distReq{ + getCost: func(dp distPeer) uint64 { + peer := dp.(*peer) + return peer.GetRequestCost(GetBlockHeadersMsg, amount) + }, + canSend: func(dp distPeer) bool { + return dp.(*peer) == p + }, + request: func(dp distPeer) func() { + peer := dp.(*peer) + cost := peer.GetRequestCost(GetBlockHeadersMsg, amount) + peer.fcServer.QueueRequest(reqID, cost) + return func() { peer.RequestHeadersByNumber(reqID, cost, origin, amount, skip, reverse) } + }, + } + _, ok := <-pm.reqDist.queue(rq) + if !ok { + return ErrNoPeers + } + return nil } if err := pm.downloader.RegisterPeer(p.id, ethVersion, p.HeadAndTd, requestHeadersByHash, requestHeadersByNumber, nil, nil, nil); err != nil { @@ -884,7 +926,13 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { } if deliverMsg != nil { - return pm.odr.Deliver(p, deliverMsg) + err := pm.odr.Deliver(p, deliverMsg) + if err != nil { + p.responseErrors++ + if p.responseErrors > maxResponseErrors { + return err + } + } } return nil } diff --git a/les/helper_test.go b/les/helper_test.go index f6293ad1a..7e442c131 100644 --- a/les/helper_test.go +++ b/les/helper_test.go @@ -28,6 +28,7 @@ import ( "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/types" "github.com/ethereum/go-ethereum/core/vm" @@ -39,7 +40,6 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) var ( @@ -134,28 +134,31 @@ func testRCL() RequestCostList { // channels for different events. func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *core.BlockGen)) (*ProtocolManager, ethdb.Database, *LesOdr, error) { var ( - evmux = new(event.TypeMux) - pow = new(pow.FakePow) - db, _ = ethdb.NewMemDatabase() - genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) - chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker - odr *LesOdr - chain BlockChain + evmux = new(event.TypeMux) + engine = ethash.NewFaker() + db, _ = ethdb.NewMemDatabase() + gspec = core.Genesis{ + Config: params.TestChainConfig, + Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + } + genesis = gspec.MustCommit(db) + odr *LesOdr + chain BlockChain ) if lightSync { odr = NewLesOdr(db) - chain, _ = light.NewLightChain(odr, chainConfig, pow, evmux) + chain, _ = light.NewLightChain(odr, gspec.Config, engine, evmux) } else { - blockchain, _ := core.NewBlockChain(db, chainConfig, pow, evmux, vm.Config{}) - gchain, _ := core.GenerateChain(chainConfig, genesis, db, blocks, generator) + blockchain, _ := core.NewBlockChain(db, gspec.Config, engine, evmux, vm.Config{}) + gchain, _ := core.GenerateChain(gspec.Config, genesis, db, blocks, generator) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) } chain = blockchain } - pm, err := NewProtocolManager(chainConfig, lightSync, NetworkId, evmux, pow, chain, nil, db, odr, nil) + pm, err := NewProtocolManager(gspec.Config, lightSync, NetworkId, evmux, engine, chain, nil, db, odr, nil) if err != nil { return nil, nil, nil, err } @@ -352,11 +355,15 @@ func (p *testServerPool) setPeer(peer *peer) { p.peer = peer } -func (p *testServerPool) selectPeerWait(uint64, func(*peer) (bool, time.Duration), <-chan struct{}) *peer { +func (p *testServerPool) getAllPeers() map[distPeer]struct{} { p.lock.RLock() defer p.lock.RUnlock() - return p.peer + m := make(map[distPeer]struct{}) + if p.peer != nil { + m[p.peer] = struct{}{} + } + return m } func (p *testServerPool) adjustResponseTime(*poolEntry, time.Duration, bool) { diff --git a/les/odr.go b/les/odr.go index afc894ab5..684f36c76 100644 --- a/les/odr.go +++ b/les/odr.go @@ -17,6 +17,7 @@ package les import ( + "context" "crypto/rand" "encoding/binary" "sync" @@ -26,20 +27,17 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" - "golang.org/x/net/context" ) var ( softRequestTimeout = time.Millisecond * 500 hardRequestTimeout = time.Second * 10 - retryPeers = time.Second * 1 ) // peerDropFn is a callback type for dropping a peer detected as malicious. type peerDropFn func(id string) type odrPeerSelector interface { - selectPeerWait(uint64, func(*peer) (bool, time.Duration), <-chan struct{}) *peer adjustResponseTime(*poolEntry, time.Duration, bool) } @@ -51,6 +49,7 @@ type LesOdr struct { mlock, clock sync.Mutex sentReqs map[uint64]*sentReq serverPool odrPeerSelector + reqDist *requestDistributor } func NewLesOdr(db ethdb.Database) *LesOdr { @@ -165,69 +164,81 @@ func (self *LesOdr) requestPeer(req *sentReq, peer *peer, delivered, timeout cha func (self *LesOdr) networkRequest(ctx context.Context, lreq LesOdrRequest) error { answered := make(chan struct{}) req := &sentReq{ - valFunc: lreq.Valid, + valFunc: lreq.Validate, sentTo: make(map[*peer]chan struct{}), answered: answered, // reply delivered by any peer } - reqID := getNextReqID() - self.mlock.Lock() - self.sentReqs[reqID] = req - self.mlock.Unlock() + + exclude := make(map[*peer]struct{}) reqWg := new(sync.WaitGroup) reqWg.Add(1) defer reqWg.Done() - go func() { - reqWg.Wait() - self.mlock.Lock() - delete(self.sentReqs, reqID) - self.mlock.Unlock() - }() - exclude := make(map[*peer]struct{}) - for { - var p *peer - if self.serverPool != nil { - p = self.serverPool.selectPeerWait(reqID, func(p *peer) (bool, time.Duration) { - if _, ok := exclude[p]; ok || !lreq.CanSend(p) { - return false, 0 - } - return true, p.fcServer.CanSend(lreq.GetCost(p)) - }, ctx.Done()) - } - if p == nil { - select { - case <-ctx.Done(): - return ctx.Err() - case <-req.answered: - return nil - case <-time.After(retryPeers): - } - } else { + var timeout chan struct{} + reqID := getNextReqID() + rq := &distReq{ + getCost: func(dp distPeer) uint64 { + return lreq.GetCost(dp.(*peer)) + }, + canSend: func(dp distPeer) bool { + p := dp.(*peer) + _, ok := exclude[p] + return !ok && lreq.CanSend(p) + }, + request: func(dp distPeer) func() { + p := dp.(*peer) exclude[p] = struct{}{} delivered := make(chan struct{}) - timeout := make(chan struct{}) + timeout = make(chan struct{}) req.lock.Lock() req.sentTo[p] = delivered req.lock.Unlock() reqWg.Add(1) cost := lreq.GetCost(p) - p.fcServer.SendRequest(reqID, cost) + p.fcServer.QueueRequest(reqID, cost) go self.requestPeer(req, p, delivered, timeout, reqWg) - lreq.Request(reqID, p) - - select { - case <-ctx.Done(): - return ctx.Err() - case <-answered: - return nil - case <-timeout: + return func() { lreq.Request(reqID, p) } + }, + } + + self.mlock.Lock() + self.sentReqs[reqID] = req + self.mlock.Unlock() + + go func() { + reqWg.Wait() + self.mlock.Lock() + delete(self.sentReqs, reqID) + self.mlock.Unlock() + }() + + for { + peerChn := self.reqDist.queue(rq) + select { + case <-ctx.Done(): + self.reqDist.cancel(rq) + return ctx.Err() + case <-answered: + self.reqDist.cancel(rq) + return nil + case _, ok := <-peerChn: + if !ok { + return ErrNoPeers } } + + select { + case <-ctx.Done(): + return ctx.Err() + case <-answered: + return nil + case <-timeout: + } } } -// Retrieve tries to fetch an object from the local db, then from the LES network. +// Retrieve tries to fetch an object from the LES network. // If the network retrieval was successful, it stores the object in local db. func (self *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) { lreq := LesRequest(req) diff --git a/les/odr_requests.go b/les/odr_requests.go index 53aced93c..1f853b341 100644 --- a/les/odr_requests.go +++ b/les/odr_requests.go @@ -49,7 +49,7 @@ type LesOdrRequest interface { GetCost(*peer) uint64 CanSend(*peer) bool Request(uint64, *peer) error - Valid(ethdb.Database, *Msg) error // if true, keeps the retrieved object + Validate(ethdb.Database, *Msg) error } func LesRequest(req light.OdrRequest) LesOdrRequest { @@ -92,7 +92,7 @@ func (r *BlockRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *BlockRequest) Valid(db ethdb.Database, msg *Msg) error { +func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error { log.Debug("Validating block body", "hash", r.Hash) // Ensure we have a correct message with a single block body @@ -148,7 +148,7 @@ func (r *ReceiptsRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *ReceiptsRequest) Valid(db ethdb.Database, msg *Msg) error { +func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error { log.Debug("Validating block receipts", "hash", r.Hash) // Ensure we have a correct message with a single block receipt @@ -208,7 +208,7 @@ func (r *TrieRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *TrieRequest) Valid(db ethdb.Database, msg *Msg) error { +func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error { log.Debug("Validating trie proof", "root", r.Id.Root, "key", r.Key) // Ensure we have a correct message with a single proof @@ -259,7 +259,7 @@ func (r *CodeRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *CodeRequest) Valid(db ethdb.Database, msg *Msg) error { +func (r *CodeRequest) Validate(db ethdb.Database, msg *Msg) error { log.Debug("Validating code data", "hash", r.Hash) // Ensure we have a correct message with a single code element @@ -319,7 +319,7 @@ func (r *ChtRequest) Request(reqID uint64, peer *peer) error { // Valid processes an ODR request reply message from the LES network // returns true and stores results in memory if the message was a valid reply // to the request (implementation of LesOdrRequest) -func (r *ChtRequest) Valid(db ethdb.Database, msg *Msg) error { +func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error { log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.BlockNum) // Ensure we have a correct message with a single proof element diff --git a/les/odr_test.go b/les/odr_test.go index 4f1fccb24..532de4d80 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -18,6 +18,7 @@ package les import ( "bytes" + "context" "math/big" "testing" "time" @@ -32,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/net/context" ) type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte @@ -123,7 +123,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, bc) + context := core.NewEVMContext(msg, header, bc, nil) vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) @@ -141,7 +141,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, lc) + context := core.NewEVMContext(msg, header, lc, nil) vmenv := vm.NewEVM(context, vmstate, config, vm.Config{}) //vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{}) @@ -162,8 +162,11 @@ func testOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { lpm, ldb, odr := newTestProtocolManagerMust(t, true, 0, nil) _, err1, lpeer, err2 := newTestPeerPair("peer", protocol, pm, lpm) pool := &testServerPool{} + lpm.reqDist = newRequestDistributor(pool.getAllPeers, lpm.quitSync) + odr.reqDist = lpm.reqDist pool.setPeer(lpeer) odr.serverPool = pool + lpeer.hasBlock = func(common.Hash, uint64) bool { return true } select { case <-time.After(time.Millisecond * 100): case err := <-err1: @@ -178,8 +181,11 @@ func testOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { for i := uint64(0); i <= pm.blockchain.CurrentHeader().Number.Uint64(); i++ { bhash := core.GetCanonicalHash(db, i) b1 := fn(light.NoOdr, db, pm.chainConfig, pm.blockchain.(*core.BlockChain), nil, bhash) - ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) + + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + defer cancel() b2 := fn(ctx, ldb, lpm.chainConfig, nil, lpm.blockchain.(*light.LightChain), bhash) + eq := bytes.Equal(b1, b2) exp := i < expFail if exp && !eq { diff --git a/les/peer.go b/les/peer.go index ef5f8a6ce..f45605593 100644 --- a/les/peer.go +++ b/les/peer.go @@ -22,6 +22,7 @@ import ( "fmt" "math/big" "sync" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -37,7 +38,10 @@ var ( errNotRegistered = errors.New("peer is not registered") ) -const maxHeadInfoLen = 20 +const ( + maxHeadInfoLen = 20 + maxResponseErrors = 50 // number of invalid responses tolerated (makes the protocol less brittle but still avoids spam) +) type peer struct { *p2p.Peer @@ -53,9 +57,11 @@ type peer struct { lock sync.RWMutex announceChn chan announceData + sendQueue *execQueue - poolEntry *poolEntry - hasBlock func(common.Hash, uint64) bool + poolEntry *poolEntry + hasBlock func(common.Hash, uint64) bool + responseErrors int fcClient *flowcontrol.ClientNode // nil if the peer is server only fcServer *flowcontrol.ServerNode // nil if the peer is client only @@ -76,6 +82,14 @@ func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { } } +func (p *peer) canQueue() bool { + return p.sendQueue.canQueue() +} + +func (p *peer) queueSend(f func()) { + p.sendQueue.queue(f) +} + // Info gathers and returns a collection of metadata known about a peer. func (p *peer) Info() *eth.PeerInfo { return ð.PeerInfo{ @@ -117,6 +131,11 @@ func (p *peer) Td() *big.Int { return new(big.Int).Set(p.headInfo.Td) } +// waitBefore implements distPeer interface +func (p *peer) waitBefore(maxCost uint64) (time.Duration, float64) { + return p.fcServer.CanSend(maxCost) +} + func sendRequest(w p2p.MsgWriter, msgcode, reqID, cost uint64, data interface{}) error { type req struct { ReqID uint64 @@ -237,11 +256,8 @@ func (p *peer) RequestHeaderProofs(reqID, cost uint64, reqs []*ChtReq) error { return sendRequest(p.rw, GetHeaderProofsMsg, reqID, cost, reqs) } -func (p *peer) SendTxs(cost uint64, txs types.Transactions) error { +func (p *peer) SendTxs(reqID, cost uint64, txs types.Transactions) error { p.Log().Debug("Fetching batch of transactions", "count", len(txs)) - reqID := getNextReqID() - p.fcServer.MustAssignRequest(reqID) - p.fcServer.SendRequest(reqID, cost) return p2p.Send(p.rw, SendTxMsg, txs) } @@ -375,9 +391,10 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version) } if server != nil { - if recv.get("serveStateSince", nil) == nil { + // until we have a proper peer connectivity API, allow LES connection to other servers + /*if recv.get("serveStateSince", nil) == nil { return errResp(ErrUselessPeer, "wanted client, got server") - } + }*/ p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams) } else { if recv.get("serveChainSince", nil) != nil { @@ -444,6 +461,7 @@ func (ps *peerSet) Register(p *peer) error { return errAlreadyRegistered } ps.peers[p.id] = p + p.sendQueue = newExecQueue(100) return nil } @@ -453,8 +471,10 @@ func (ps *peerSet) Unregister(id string) error { ps.lock.Lock() defer ps.lock.Unlock() - if _, ok := ps.peers[id]; !ok { + if p, ok := ps.peers[id]; !ok { return errNotRegistered + } else { + p.sendQueue.quit() } delete(ps.peers, id) return nil diff --git a/les/request_test.go b/les/request_test.go index 10e9edf8b..ba1fc15bd 100644 --- a/les/request_test.go +++ b/les/request_test.go @@ -17,6 +17,7 @@ package les import ( + "context" "testing" "time" @@ -25,7 +26,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" - "golang.org/x/net/context" ) var testBankSecureTrieKey = secAddr(testBankAddress) @@ -72,8 +72,11 @@ func testAccess(t *testing.T, protocol int, fn accessTestFn) { lpm, ldb, odr := newTestProtocolManagerMust(t, true, 0, nil) _, err1, lpeer, err2 := newTestPeerPair("peer", protocol, pm, lpm) pool := &testServerPool{} + lpm.reqDist = newRequestDistributor(pool.getAllPeers, lpm.quitSync) + odr.reqDist = lpm.reqDist pool.setPeer(lpeer) odr.serverPool = pool + lpeer.hasBlock = func(common.Hash, uint64) bool { return true } select { case <-time.After(time.Millisecond * 100): case err := <-err1: @@ -88,7 +91,9 @@ func testAccess(t *testing.T, protocol int, fn accessTestFn) { for i := uint64(0); i <= pm.blockchain.CurrentHeader().Number.Uint64(); i++ { bhash := core.GetCanonicalHash(db, i) if req := fn(ldb, bhash, i); req != nil { - ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + defer cancel() + err := odr.Retrieve(ctx, req) got := err == nil exp := i < expFail diff --git a/les/server.go b/les/server.go index 04bfa0292..22fe59b7a 100644 --- a/les/server.go +++ b/les/server.go @@ -45,7 +45,7 @@ type LesServer struct { } func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { - pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, eth.EventMux(), eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil) + pm, err := NewProtocolManager(eth.BlockChain().Config(), false, config.NetworkId, eth.EventMux(), eth.Engine(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil) if err != nil { return nil, err } diff --git a/les/serverpool.go b/les/serverpool.go index 55d481dbf..64fe991c6 100644 --- a/les/serverpool.go +++ b/les/serverpool.go @@ -268,82 +268,6 @@ func (pool *serverPool) adjustResponseTime(entry *poolEntry, time time.Duration, } } -type selectPeerItem struct { - peer *peer - weight int64 - wait time.Duration -} - -func (sp selectPeerItem) Weight() int64 { - return sp.weight -} - -// selectPeer selects a suitable peer for a request, also returning a necessary waiting time to perform the request -// and a "locked" flag meaning that the request has been assigned to the given peer and its execution is guaranteed -// after the given waiting time. If locked flag is false, selectPeer should be called again after the waiting time. -func (pool *serverPool) selectPeer(reqID uint64, canSend func(*peer) (bool, time.Duration)) (*peer, time.Duration, bool) { - pool.lock.Lock() - type selectPeer struct { - peer *peer - rstat, tstat float64 - } - var list []selectPeer - sel := newWeightedRandomSelect() - for _, entry := range pool.entries { - if entry.state == psRegistered { - if !entry.peer.fcServer.IsAssigned() { - list = append(list, selectPeer{entry.peer, entry.responseStats.recentAvg(), entry.timeoutStats.recentAvg()}) - } - } - } - pool.lock.Unlock() - - for _, sp := range list { - ok, wait := canSend(sp.peer) - if ok { - w := int64(1000000000 * (peerSelectMinWeight + math.Exp(-(sp.rstat+float64(wait))/float64(responseScoreTC))*math.Pow((1-sp.tstat), timeoutPow))) - sel.update(selectPeerItem{peer: sp.peer, weight: w, wait: wait}) - } - } - choice := sel.choose() - if choice == nil { - return nil, 0, false - } - peer, wait := choice.(selectPeerItem).peer, choice.(selectPeerItem).wait - locked := false - if wait < time.Millisecond*100 { - if peer.fcServer.AssignRequest(reqID) { - ok, w := canSend(peer) - wait = time.Duration(w) - if ok && wait < time.Millisecond*100 { - locked = true - } else { - peer.fcServer.DeassignRequest(reqID) - wait = time.Millisecond * 100 - } - } - } else { - wait = time.Millisecond * 100 - } - return peer, wait, locked -} - -// selectPeer selects a suitable peer for a request, waiting until an assignment to -// the request is guaranteed or the process is aborted. -func (pool *serverPool) selectPeerWait(reqID uint64, canSend func(*peer) (bool, time.Duration), abort <-chan struct{}) *peer { - for { - peer, wait, locked := pool.selectPeer(reqID, canSend) - if locked { - return peer - } - select { - case <-abort: - return nil - case <-time.After(wait): - } - } -} - // eventLoop handles pool events and mutex locking for all internal functions func (pool *serverPool) eventLoop() { lookupCnt := 0 diff --git a/les/sync.go b/les/sync.go index c143cb145..c0e17f97d 100644 --- a/les/sync.go +++ b/les/sync.go @@ -17,12 +17,12 @@ package les import ( + "context" "time" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/light" - "golang.org/x/net/context" ) const ( @@ -77,8 +77,8 @@ func (pm *ProtocolManager) synchronise(peer *peer) { return } - ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() pm.blockchain.(*light.LightChain).SyncCht(ctx) - pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync) } diff --git a/les/txrelay.go b/les/txrelay.go index 76d416c57..1ca3467e4 100644 --- a/les/txrelay.go +++ b/les/txrelay.go @@ -35,13 +35,14 @@ type LesTxRelay struct { peerList []*peer peerStartPos int lock sync.RWMutex + + reqDist *requestDistributor } func NewLesTxRelay() *LesTxRelay { return &LesTxRelay{ txSent: make(map[common.Hash]*ltrInfo), txPending: make(map[common.Hash]struct{}), - ps: newPeerSet(), } } @@ -108,10 +109,26 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) { } for p, list := range sendTo { - cost := p.GetRequestCost(SendTxMsg, len(list)) - go func(p *peer, list types.Transactions, cost uint64) { - p.SendTxs(cost, list) - }(p, list, cost) + pp := p + ll := list + + reqID := getNextReqID() + rq := &distReq{ + getCost: func(dp distPeer) uint64 { + peer := dp.(*peer) + return peer.GetRequestCost(SendTxMsg, len(ll)) + }, + canSend: func(dp distPeer) bool { + return dp.(*peer) == pp + }, + request: func(dp distPeer) func() { + peer := dp.(*peer) + cost := peer.GetRequestCost(SendTxMsg, len(ll)) + peer.fcServer.QueueRequest(reqID, cost) + return func() { peer.SendTxs(reqID, cost, ll) } + }, + } + self.reqDist.queue(rq) } } diff --git a/light/lightchain.go b/light/lightchain.go index 4370dc0fc..e8fd0ba5e 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -17,21 +17,22 @@ package light import ( + "context" "math/big" "sync" "sync/atomic" + "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/hashicorp/golang-lru" - "golang.org/x/net/context" ) var ( @@ -63,14 +64,13 @@ type LightChain struct { procInterrupt int32 // interrupt signaler for block processing wg sync.WaitGroup - pow pow.PoW - validator core.HeaderValidator + engine consensus.Engine } // 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, pow pow.PoW, mux *event.TypeMux) (*LightChain, error) { +func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine, mux *event.TypeMux) (*LightChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) @@ -83,25 +83,17 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux bodyCache: bodyCache, bodyRLPCache: bodyRLPCache, blockCache: blockCache, - pow: pow, + engine: engine, } - var err error - bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.Validator, bc.getProcInterrupt) - bc.SetValidator(core.NewHeaderValidator(config, bc.hc, pow)) + bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt) if err != nil { return nil, err } - bc.genesisBlock, _ = bc.GetBlockByNumber(NoOdr, 0) if bc.genesisBlock == nil { - bc.genesisBlock, err = core.WriteDefaultGenesisBlock(odr.Database()) - if err != nil { - return nil, err - } - log.Warn("Wrote default ethereum genesis block") + return nil, core.ErrNoGenesis } - if bc.genesisBlock.Hash() == params.MainNetGenesisHash { // add trusted CHT WriteTrustedCht(bc.chainDb, TrustedCht{Number: 805, Root: common.HexToHash("85e4286fe0a730390245c49de8476977afdae0eb5530b277f62a52b12313d50f")}) @@ -148,9 +140,6 @@ func (self *LightChain) loadLastState() error { headerTd := self.GetTd(header.Hash(), header.Number.Uint64()) log.Info("Loaded most recent local header", "number", header.Number, "hash", header.Hash(), "td", headerTd) - // Try to be smart and issue a pow verification for the head to pre-generate caches - go self.pow.Verify(types.NewBlockWithHeader(header)) - return nil } @@ -191,20 +180,6 @@ func (self *LightChain) Status() (td *big.Int, currentBlock common.Hash, genesis return self.GetTd(hash, header.Number.Uint64()), hash, self.genesisBlock.Hash() } -// SetValidator sets the validator which is used to validate incoming headers. -func (self *LightChain) SetValidator(validator core.HeaderValidator) { - self.procmu.Lock() - defer self.procmu.Unlock() - self.validator = validator -} - -// Validator returns the current header validator. -func (self *LightChain) Validator() core.HeaderValidator { - self.procmu.RLock() - defer self.procmu.RUnlock() - return self.validator -} - // State returns a new mutable state based on the current HEAD block. func (self *LightChain) State() *LightState { return NewLightState(StateTrieID(self.hc.CurrentHeader()), self.odr) @@ -238,6 +213,9 @@ func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) { // Accessors +// Engine retrieves the light chain's consensus engine. +func (bc *LightChain) Engine() consensus.Engine { return bc.engine } + // Genesis returns the genesis block func (bc *LightChain) Genesis() *types.Block { return bc.genesisBlock @@ -369,9 +347,17 @@ func (self *LightChain) postChainEvents(events []interface{}) { // In the case of a light chain, InsertHeaderChain also creates and posts light // chain events when necessary. func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { + start := time.Now() + if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil { + return i, err + } + // Make sure only one thread manipulates the chain at once self.chainmu.Lock() - defer self.chainmu.Unlock() + defer func() { + self.chainmu.Unlock() + time.Sleep(time.Millisecond * 10) // ugly hack; do not hog chain lock in case syncing is CPU-limited by validation + }() self.wg.Add(1) defer self.wg.Done() @@ -397,7 +383,7 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) } return err } - i, err := self.hc.InsertHeaderChain(chain, checkFreq, whFunc) + i, err := self.hc.InsertHeaderChain(chain, whFunc, start) go self.postChainEvents(events) return i, err } diff --git a/light/lightchain_test.go b/light/lightchain_test.go index 8a99c69f1..21b621046 100644 --- a/light/lightchain_test.go +++ b/light/lightchain_test.go @@ -17,20 +17,18 @@ package light import ( + "context" "fmt" "math/big" - "runtime" "testing" "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/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" - "github.com/hashicorp/golang-lru" - "golang.org/x/net/context" ) // So we can deterministically seed different blockchains @@ -51,22 +49,15 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [ return headers } -func testChainConfig() *params.ChainConfig { - return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} -} - // newCanonical creates a chain database, and injects a deterministic canonical // chain. Depending on the full flag, if creates either a full block chain or a // header only chain. func newCanonical(n int) (ethdb.Database, *LightChain, error) { - // Create te new chain database db, _ := ethdb.NewMemDatabase() - evmux := &event.TypeMux{} + gspec := core.Genesis{Config: params.TestChainConfig} + genesis := gspec.MustCommit(db) + blockchain, _ := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFaker(), new(event.TypeMux)) - // Initialize a fresh chain with only a genesis block - genesis, _ := core.WriteTestNetGenesisBlock(db) - - blockchain, _ := NewLightChain(&dummyOdr{db: db}, testChainConfig(), pow.FakePow{}, evmux) // Create and inject the requested chain if n == 0 { return db, blockchain, nil @@ -77,21 +68,19 @@ func newCanonical(n int) (ethdb.Database, *LightChain, error) { return db, blockchain, err } -func init() { - runtime.GOMAXPROCS(runtime.NumCPU()) -} - -func theLightChain(db ethdb.Database, t *testing.T) *LightChain { - var eventMux event.TypeMux - core.WriteTestNetGenesisBlock(db) - LightChain, err := NewLightChain(&dummyOdr{db: db}, testChainConfig(), pow.NewTestEthash(), &eventMux) +// newTestLightChain creates a LightChain that doesn't validate anything. +func newTestLightChain() *LightChain { + db, _ := ethdb.NewMemDatabase() + gspec := &core.Genesis{ + Difficulty: big.NewInt(1), + Config: params.TestChainConfig, + } + gspec.MustCommit(db) + lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker(), new(event.TypeMux)) if err != nil { - t.Error("failed creating LightChain:", err) - t.FailNow() - return nil + panic(err) } - - return LightChain + return lc } // Test fork of length N starting from block i @@ -137,17 +126,17 @@ func printChain(bc *LightChain) { // testHeaderChainImport tries to process a chain of header, writing them into // the database if successful. -func testHeaderChainImport(chain []*types.Header, LightChain *LightChain) error { +func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error { for _, header := range chain { // Try and validate the header - if err := LightChain.Validator().ValidateHeader(header, LightChain.GetHeaderByHash(header.ParentHash), false); err != nil { + if err := lightchain.engine.VerifyHeader(lightchain.hc, header, true); err != nil { return err } // Manually insert the header into the database, but don't reorganize (allows subsequent testing) - LightChain.mu.Lock() - core.WriteTd(LightChain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, LightChain.GetTdByHash(header.ParentHash))) - core.WriteHeader(LightChain.chainDb, header) - LightChain.mu.Unlock() + lightchain.mu.Lock() + core.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash))) + core.WriteHeader(lightchain.chainDb, header) + lightchain.mu.Unlock() } return nil } @@ -264,10 +253,6 @@ func TestBrokenHeaderChain(t *testing.T) { } } -type bproc struct{} - -func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil } - func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { var chain []*types.Header for i, difficulty := range d { @@ -302,20 +287,6 @@ func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error { return nil } -func chm(genesis *types.Block, db ethdb.Database) *LightChain { - odr := &dummyOdr{db: db} - var eventMux event.TypeMux - bc := &LightChain{odr: odr, chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: pow.FakePow{}} - bc.hc, _ = core.NewHeaderChain(db, testChainConfig(), bc.Validator, bc.getProcInterrupt) - bc.bodyCache, _ = lru.New(100) - bc.bodyRLPCache, _ = lru.New(100) - bc.blockCache, _ = lru.New(100) - bc.SetValidator(bproc{}) - bc.ResetWithGenesisBlock(genesis) - - return bc -} - // Tests that reorganizing a long difficult chain after a short easy one // overwrites the canonical numbers and links in the database. func TestReorgLongHeaders(t *testing.T) { @@ -329,14 +300,11 @@ func TestReorgShortHeaders(t *testing.T) { } func testReorg(t *testing.T, first, second []int, td int64) { - // Create a pristine block chain - db, _ := ethdb.NewMemDatabase() - genesis, _ := core.WriteTestNetGenesisBlock(db) - bc := chm(genesis, db) + bc := newTestLightChain() // Insert an easy and a difficult chain afterwards - bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, first, 11), 1) - bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, second, 22), 1) + bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1) + bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1) // Check that the chain is valid number and link wise prev := bc.CurrentHeader() for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) { @@ -345,7 +313,7 @@ func testReorg(t *testing.T, first, second []int, td int64) { } } // Make sure the chain total difficulty is the correct one - want := new(big.Int).Add(genesis.Difficulty(), big.NewInt(td)) + want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td)) if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { t.Errorf("total difficulty mismatch: have %v, want %v", have, want) } @@ -353,31 +321,24 @@ func testReorg(t *testing.T, first, second []int, td int64) { // Tests that the insertion functions detect banned hashes. func TestBadHeaderHashes(t *testing.T) { - // Create a pristine block chain - db, _ := ethdb.NewMemDatabase() - genesis, _ := core.WriteTestNetGenesisBlock(db) - bc := chm(genesis, db) + bc := newTestLightChain() // Create a chain, ban a hash and try to import var err error - headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 4}, 10) + headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10) core.BadHashes[headers[2].Hash()] = true - _, err = bc.InsertHeaderChain(headers, 1) - if !core.IsBadHashError(err) { - t.Errorf("error mismatch: want: BadHashError, have: %v", err) + if _, err = bc.InsertHeaderChain(headers, 1); err != core.ErrBlacklistedHash { + t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBlacklistedHash) } } // Tests that bad hashes are detected on boot, and the chan rolled back to a // good state prior to the bad hash. func TestReorgBadHeaderHashes(t *testing.T) { - // Create a pristine block chain - db, _ := ethdb.NewMemDatabase() - genesis, _ := core.WriteTestNetGenesisBlock(db) - bc := chm(genesis, db) + bc := newTestLightChain() // Create a chain, import and ban aferwards - headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) + headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10) if _, err := bc.InsertHeaderChain(headers, 1); err != nil { t.Fatalf("failed to import headers: %v", err) @@ -387,8 +348,9 @@ func TestReorgBadHeaderHashes(t *testing.T) { } core.BadHashes[headers[3].Hash()] = true defer func() { delete(core.BadHashes, headers[3].Hash()) }() - // Create a new chain manager and check it rolled back the state - ncm, err := NewLightChain(&dummyOdr{db: db}, testChainConfig(), pow.FakePow{}, new(event.TypeMux)) + + // Create a new LightChain and check that it rolled back the state. + ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker(), new(event.TypeMux)) if err != nil { t.Fatalf("failed to create new chain manager: %v", err) } diff --git a/light/odr.go b/light/odr.go index 4f6ef6b9e..ca6364f28 100644 --- a/light/odr.go +++ b/light/odr.go @@ -19,6 +19,7 @@ package light import ( + "context" "math/big" "github.com/ethereum/go-ethereum/common" @@ -27,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/net/context" ) // NoOdr is the default context passed to an ODR capable function when the ODR diff --git a/light/odr_test.go b/light/odr_test.go index e2eced346..576e3abc9 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -18,6 +18,7 @@ package light import ( "bytes" + "context" "errors" "math/big" "testing" @@ -25,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -33,10 +35,8 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "golang.org/x/net/context" ) var ( @@ -175,7 +175,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, bc) + context := core.NewEVMContext(msg, header, bc, nil) vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) @@ -191,7 +191,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain from.SetBalance(math.MaxBig256) msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, lc) + context := core.NewEVMContext(msg, header, lc, nil) vmenv := vm.NewEVM(context, vmstate, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) ret, _, _ := core.ApplyMessage(vmenv, msg, gp) @@ -248,23 +248,21 @@ func testChainGen(i int, block *core.BlockGen) { func testChainOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { var ( evmux = new(event.TypeMux) - pow = new(pow.FakePow) sdb, _ = ethdb.NewMemDatabase() ldb, _ = ethdb.NewMemDatabase() - genesis = core.WriteGenesisBlockForTesting(sdb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} + genesis = gspec.MustCommit(sdb) ) - core.WriteGenesisBlockForTesting(ldb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + gspec.MustCommit(ldb) // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, testChainConfig(), pow, evmux, vm.Config{}) - chainConfig := ¶ms.ChainConfig{HomesteadBlock: new(big.Int)} - gchain, _ := core.GenerateChain(chainConfig, genesis, sdb, 4, testChainGen) + blockchain, _ := core.NewBlockChain(sdb, params.TestChainConfig, ethash.NewFullFaker(), evmux, vm.Config{}) + gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, sdb, 4, testChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) } odr := &testOdr{sdb: sdb, ldb: ldb} - lightchain, _ := NewLightChain(odr, testChainConfig(), pow, evmux) - lightchain.SetValidator(bproc{}) + lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), evmux) headers := make([]*types.Header, len(gchain)) for i, block := range gchain { headers[i] = block.Header() @@ -277,8 +275,11 @@ func testChainOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { for i := uint64(0); i <= blockchain.CurrentHeader().Number.Uint64(); i++ { bhash := core.GetCanonicalHash(sdb, i) b1 := fn(NoOdr, sdb, blockchain, nil, bhash) - ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) + + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + defer cancel() b2 := fn(ctx, ldb, nil, lightchain, bhash) + eq := bytes.Equal(b1, b2) exp := i < expFail if exp && !eq { diff --git a/light/odr_util.go b/light/odr_util.go index 17e9aadcb..d7f8458f1 100644 --- a/light/odr_util.go +++ b/light/odr_util.go @@ -18,6 +18,7 @@ package light import ( "bytes" + "context" "errors" "math/big" @@ -27,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/net/context" ) var sha3_nil = crypto.Keccak256Hash(nil) diff --git a/light/state.go b/light/state.go index d3e047ef4..b184dc3a5 100644 --- a/light/state.go +++ b/light/state.go @@ -17,11 +17,11 @@ package light import ( + "context" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "golang.org/x/net/context" ) // LightState is a memory representation of a state. diff --git a/light/state_object.go b/light/state_object.go index f33ba217e..a54ea1d9f 100644 --- a/light/state_object.go +++ b/light/state_object.go @@ -18,13 +18,13 @@ package light import ( "bytes" + "context" "fmt" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/net/context" ) var emptyCodeHash = crypto.Keccak256(nil) diff --git a/light/state_test.go b/light/state_test.go index d594ab9ff..e776efec8 100644 --- a/light/state_test.go +++ b/light/state_test.go @@ -18,6 +18,7 @@ package light import ( "bytes" + "context" "math/big" "testing" @@ -26,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" - "golang.org/x/net/context" ) func makeTestState() (common.Hash, ethdb.Database) { diff --git a/light/trie.go b/light/trie.go index c5525358a..1440f2fbf 100644 --- a/light/trie.go +++ b/light/trie.go @@ -17,9 +17,10 @@ package light import ( + "context" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" - "golang.org/x/net/context" ) // LightTrie is an ODR-capable wrapper around trie.SecureTrie diff --git a/light/txpool.go b/light/txpool.go index 28c8d8ca5..446195806 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -17,6 +17,7 @@ package light import ( + "context" "fmt" "sync" "time" @@ -29,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/net/context" ) // txPermanent is the number of mined blocks after a mined transaction is @@ -230,13 +230,13 @@ func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) { } } -// setNewHead sets a new head header, processing (and rolling back if necessary) +// reorgOnNewHead sets a new head header, processing (and rolling back if necessary) // the blocks since the last known head and returns a txStateChanges map containing // the recently mined and rolled back transaction hashes. If an error (context // timeout) occurs during checking new blocks, it leaves the locally known head // at the latest checked block and still returns a valid txStateChanges, making it // possible to continue checking the missing blocks at the next chain head event -func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) { +func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) { txc := make(txStateChanges) oldh := pool.chain.GetHeaderByHash(pool.head) newh := newHeader @@ -276,15 +276,17 @@ func (pool *TxPool) setNewHead(ctx context.Context, newHeader *types.Header) (tx // clear old mined tx entries of old blocks if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent { idx2 := idx - txPermanent - for i := pool.clearIdx; i < idx2; i++ { - hash := core.GetCanonicalHash(pool.chainDb, i) - if list, ok := pool.mined[hash]; ok { - hashes := make([]common.Hash, len(list)) - for i, tx := range list { - hashes[i] = tx.Hash() + if len(pool.mined) > 0 { + for i := pool.clearIdx; i < idx2; i++ { + hash := core.GetCanonicalHash(pool.chainDb, i) + if list, ok := pool.mined[hash]; ok { + hashes := make([]common.Hash, len(list)) + for i, tx := range list { + hashes[i] = tx.Hash() + } + pool.relay.Discard(hashes) + delete(pool.mined, hash) } - pool.relay.Discard(hashes) - delete(pool.mined, hash) } } pool.clearIdx = idx2 @@ -303,19 +305,28 @@ func (pool *TxPool) eventLoop() { for ev := range pool.events.Chan() { switch ev.Data.(type) { case core.ChainHeadEvent: - pool.mu.Lock() - ctx, _ := context.WithTimeout(context.Background(), blockCheckTimeout) - head := pool.chain.CurrentHeader() - txc, _ := pool.setNewHead(ctx, head) - m, r := txc.getLists() - pool.relay.NewHead(pool.head, m, r) - pool.homestead = pool.config.IsHomestead(head.Number) - pool.signer = types.MakeSigner(pool.config, head.Number) - pool.mu.Unlock() + pool.setNewHead(ev.Data.(core.ChainHeadEvent).Block.Header()) + // hack in order to avoid hogging the lock; this part will + // be replaced by a subsequent PR. + time.Sleep(time.Millisecond) } } } +func (pool *TxPool) setNewHead(head *types.Header) { + pool.mu.Lock() + defer pool.mu.Unlock() + + ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout) + defer cancel() + + txc, _ := pool.reorgOnNewHead(ctx, head) + m, r := txc.getLists() + pool.relay.NewHead(pool.head, m, r) + pool.homestead = pool.config.IsHomestead(head.Number) + pool.signer = types.MakeSigner(pool.config, head.Number) +} + // Stop stops the light transaction pool func (pool *TxPool) Stop() { close(pool.quit) diff --git a/light/txpool_test.go b/light/txpool_test.go index 980c7c898..f23832a41 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -17,20 +17,20 @@ package light import ( + "context" "math" "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/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" - "golang.org/x/net/context" ) type testTxRelay struct { @@ -83,16 +83,15 @@ func TestTxPool(t *testing.T) { var ( evmux = new(event.TypeMux) - pow = new(pow.FakePow) sdb, _ = ethdb.NewMemDatabase() ldb, _ = ethdb.NewMemDatabase() - genesis = core.WriteGenesisBlockForTesting(sdb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} + genesis = gspec.MustCommit(sdb) ) - core.WriteGenesisBlockForTesting(ldb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) + gspec.MustCommit(ldb) // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, testChainConfig(), pow, evmux, vm.Config{}) - chainConfig := ¶ms.ChainConfig{HomesteadBlock: new(big.Int)} - gchain, _ := core.GenerateChain(chainConfig, genesis, sdb, poolTestBlocks, txPoolTestChainGen) + blockchain, _ := core.NewBlockChain(sdb, params.TestChainConfig, ethash.NewFullFaker(), evmux, vm.Config{}) + gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, sdb, poolTestBlocks, txPoolTestChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) } @@ -103,14 +102,14 @@ func TestTxPool(t *testing.T) { discard: make(chan int, 1), mined: make(chan int, 1), } - lightchain, _ := NewLightChain(odr, testChainConfig(), pow, evmux) - lightchain.SetValidator(bproc{}) + lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), evmux) txPermanent = 50 - pool := NewTxPool(testChainConfig(), evmux, lightchain, relay) + pool := NewTxPool(params.TestChainConfig, evmux, lightchain, relay) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() for ii, block := range gchain { i := ii + 1 - ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond) s := sentTx(i - 1) e := sentTx(i) for i := s; i < e; i++ { diff --git a/light/vm_env.go b/light/vm_env.go index ebd229de8..54aa12875 100644 --- a/light/vm_env.go +++ b/light/vm_env.go @@ -17,12 +17,12 @@ package light import ( + "context" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "golang.org/x/net/context" ) // VMState is a wrapper for the light state that holds the actual context and diff --git a/log/format.go b/log/format.go index e315b5237..6c19c7a55 100644 --- a/log/format.go +++ b/log/format.go @@ -133,10 +133,10 @@ func TerminalFormat(usecolor bool) Format { } } // try to justify the log output for short messages - if len(r.Ctx) > 0 && len(r.Msg) < termMsgJust { - b.Write(bytes.Repeat([]byte{' '}, termMsgJust-len(r.Msg))) + length := utf8.RuneCountInString(r.Msg) + if len(r.Ctx) > 0 && length < termMsgJust { + b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length)) } - // print the keys logfmt style logfmt(b, r.Ctx, color, true) return b.Bytes() diff --git a/log/handler.go b/log/handler.go index abb17b4c4..d5594b853 100644 --- a/log/handler.go +++ b/log/handler.go @@ -106,11 +106,16 @@ func CallerFileHandler(h Handler) Handler { // the context with key "fn". func CallerFuncHandler(h Handler) Handler { return FuncHandler(func(r *Record) error { - r.Ctx = append(r.Ctx, "fn", fmt.Sprintf("%+n", r.Call)) + r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call)) return h.Log(r) }) } +// This function is here to please go vet on Go < 1.8. +func formatCall(format string, c stack.Call) string { + return fmt.Sprintf(format, c) +} + // CallerStackHandler returns a Handler that adds a stack trace to the context // with key "stack". The stack trace is formated as a space separated list of // call sites inside matching []'s. The most recent call site is listed first. diff --git a/miner/agent.go b/miner/agent.go index 3c407f20b..855892a07 100644 --- a/miner/agent.go +++ b/miner/agent.go @@ -17,56 +17,49 @@ package miner import ( - "fmt" "sync" "sync/atomic" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/pow" ) type CpuAgent struct { mu sync.Mutex workCh chan *Work - quit chan struct{} + stop chan struct{} quitCurrentOp chan struct{} returnCh chan<- *Result - index int - pow pow.PoW + chain consensus.ChainReader + engine consensus.Engine isMining int32 // isMining indicates whether the agent is currently mining } -func NewCpuAgent(index int, pow pow.PoW) *CpuAgent { +func NewCpuAgent(chain consensus.ChainReader, engine consensus.Engine) *CpuAgent { miner := &CpuAgent{ - pow: pow, - index: index, - quit: make(chan struct{}), + chain: chain, + engine: engine, + stop: make(chan struct{}, 1), workCh: make(chan *Work, 1), } - return miner } func (self *CpuAgent) Work() chan<- *Work { return self.workCh } -func (self *CpuAgent) Pow() pow.PoW { return self.pow } func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch } func (self *CpuAgent) Stop() { - close(self.quit) + self.stop <- struct{}{} } func (self *CpuAgent) Start() { - if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) { return // agent already started } - go self.update() } @@ -82,7 +75,7 @@ out: self.quitCurrentOp = make(chan struct{}) go self.mine(work, self.quitCurrentOp) self.mu.Unlock() - case <-self.quit: + case <-self.stop: self.mu.Lock() if self.quitCurrentOp != nil { close(self.quitCurrentOp) @@ -99,27 +92,27 @@ done: select { case <-self.workCh: default: - close(self.workCh) break done } } - atomic.StoreInt32(&self.isMining, 0) } func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) { - log.Debug(fmt.Sprintf("(re)started agent[%d]. mining...\n", self.index)) - - // Mine - nonce, mixDigest := self.pow.Search(work.Block, stop) - if nonce != 0 { - block := work.Block.WithMiningResult(types.EncodeNonce(nonce), common.BytesToHash(mixDigest)) - self.returnCh <- &Result{work, block} + if result, err := self.engine.Seal(self.chain, work.Block, stop); result != nil { + log.Info("Successfully sealed new block", "number", result.Number(), "hash", result.Hash()) + self.returnCh <- &Result{work, result} } else { + if err != nil { + log.Warn("Block sealing failed", "err", err) + } self.returnCh <- nil } } func (self *CpuAgent) GetHashRate() int64 { - return int64(self.pow.Hashrate()) + if pow, ok := self.engine.(consensus.PoW); ok { + return int64(pow.Hashrate()) + } + return 0 } diff --git a/miner/miner.go b/miner/miner.go index dc0591b9a..453fff04d 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -32,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" ) // Backend wraps all methods required for mining. @@ -49,24 +49,24 @@ type Miner struct { worker *worker - threads int coinbase common.Address mining int32 eth Backend - pow pow.PoW + engine consensus.Engine canStart int32 // can start indicates whether we can start the mining operation shouldStart int32 // should start indicates whether we should start after sync } -func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { +func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner { miner := &Miner{ eth: eth, mux: mux, - pow: pow, - worker: newWorker(config, common.Address{}, eth, mux), + engine: engine, + worker: newWorker(config, engine, common.Address{}, eth, mux), canStart: 1, } + miner.Register(NewCpuAgent(eth.BlockChain(), engine)) go miner.update() return miner @@ -86,7 +86,7 @@ out: if self.Mining() { self.Stop() atomic.StoreInt32(&self.shouldStart, 1) - log.Info(fmt.Sprint("Mining operation aborted due to sync operation")) + log.Info("Mining aborted due to sync") } case downloader.DoneEvent, downloader.FailedEvent: shouldStart := atomic.LoadInt32(&self.shouldStart) == 1 @@ -94,7 +94,7 @@ out: atomic.StoreInt32(&self.canStart, 1) atomic.StoreInt32(&self.shouldStart, 0) if shouldStart { - self.Start(self.coinbase, self.threads) + self.Start(self.coinbase) } // unsubscribe. we're only interested in this event once events.Unsubscribe() @@ -116,23 +116,18 @@ func (m *Miner) SetGasPrice(price *big.Int) { m.worker.setGasPrice(price) } -func (self *Miner) Start(coinbase common.Address, threads int) { +func (self *Miner) Start(coinbase common.Address) { atomic.StoreInt32(&self.shouldStart, 1) self.worker.setEtherbase(coinbase) self.coinbase = coinbase - self.threads = threads if atomic.LoadInt32(&self.canStart) == 0 { - log.Info(fmt.Sprint("Can not start mining operation due to network sync (starts when finished)")) + log.Info("Network syncing, will start miner afterwards") return } atomic.StoreInt32(&self.mining, 1) - for i := 0; i < threads; i++ { - self.worker.register(NewCpuAgent(i, self.pow)) - } - - log.Info(fmt.Sprintf("Starting mining operation (CPU=%d TOT=%d)\n", threads, len(self.worker.agents))) + log.Info("Starting mining operation") self.worker.start() self.worker.commitNewWork() } @@ -159,7 +154,9 @@ func (self *Miner) Mining() bool { } func (self *Miner) HashRate() (tot int64) { - tot += int64(self.pow.Hashrate()) + if pow, ok := self.engine.(consensus.PoW); ok { + tot += int64(pow.Hashrate()) + } // do we care this might race? is it worth we're rewriting some // aspects of the worker/locking up agents so we can get an accurate // hashrate? diff --git a/miner/remote_agent.go b/miner/remote_agent.go index 08c5fc6f0..bb223ba1b 100644 --- a/miner/remote_agent.go +++ b/miner/remote_agent.go @@ -18,16 +18,16 @@ package miner import ( "errors" - "fmt" "math/big" "sync" "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/pow" ) type hashrate struct { @@ -42,7 +42,8 @@ type RemoteAgent struct { workCh chan *Work returnCh chan<- *Result - pow pow.PoW + chain consensus.ChainReader + engine consensus.Engine currentWork *Work work map[common.Hash]*Work @@ -52,9 +53,10 @@ type RemoteAgent struct { running int32 // running indicates whether the agent is active. Call atomically } -func NewRemoteAgent(pow pow.PoW) *RemoteAgent { +func NewRemoteAgent(chain consensus.ChainReader, engine consensus.Engine) *RemoteAgent { return &RemoteAgent{ - pow: pow, + chain: chain, + engine: engine, work: make(map[common.Hash]*Work), hashrate: make(map[common.Hash]hashrate), } @@ -114,7 +116,7 @@ func (a *RemoteAgent) GetWork() ([3]string, error) { block := a.currentWork.Block res[0] = block.HashNoNonce().Hex() - seedHash := pow.EthashSeedHash(block.NumberU64()) + seedHash := ethash.SeedHash(block.NumberU64()) res[1] = common.BytesToHash(seedHash).Hex() // Calculate the "target" to be returned to the external miner n := big.NewInt(1) @@ -129,8 +131,8 @@ func (a *RemoteAgent) GetWork() ([3]string, error) { return res, errors.New("No work available yet, don't panic.") } -// SubmitWork tries to inject a PoW solution tinto the remote agent, returning -// whether the solution was acceted or not (not can be both a bad PoW as well as +// SubmitWork tries to inject a pow solution into the remote agent, returning +// whether the solution was accepted or not (not can be both a bad pow as well as // any other error, like no work pending). func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool { a.mu.Lock() @@ -139,15 +141,20 @@ func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common. // Make sure the work submitted is present work := a.work[hash] if work == nil { - log.Info(fmt.Sprintf("Work was submitted for %x but no pending work found", hash)) + log.Info("Work submitted but none pending", "hash", hash) return false } - // Make sure the PoW solutions is indeed valid - block := work.Block.WithMiningResult(nonce, mixDigest) - if err := a.pow.Verify(block); err != nil { - log.Warn(fmt.Sprintf("Invalid PoW submitted for %x: %v", hash, err)) + // Make sure the Engine solutions is indeed valid + result := work.Block.Header() + result.Nonce = nonce + result.MixDigest = mixDigest + + if err := a.engine.VerifySeal(a.chain, result); err != nil { + log.Warn("Invalid proof-of-work submitted", "hash", hash, "err", err) return false } + block := work.Block.WithSeal(result) + // Solutions seems to be valid, return to the miner and notify acceptance a.returnCh <- &Result{work, block} delete(a.work, hash) diff --git a/miner/unconfirmed.go b/miner/unconfirmed.go index bb7d0ff26..ee52d8512 100644 --- a/miner/unconfirmed.go +++ b/miner/unconfirmed.go @@ -18,7 +18,6 @@ package miner import ( "container/ring" - "fmt" "sync" "github.com/ethereum/go-ethereum/common" @@ -80,7 +79,7 @@ func (set *unconfirmedBlocks) Insert(index uint64, hash common.Hash) { set.blocks.Move(-1).Link(item) } // Display a log for the user to notify of a new mined block unconfirmed - log.Info(fmt.Sprintf("🔨 mined potential block #%d [%x…], waiting for %d blocks to confirm", index, hash.Bytes()[:4], set.depth)) + log.Info("🔨 mined potential block", "number", index, "hash", hash) } // Shift drops all unconfirmed blocks from the set which exceed the unconfirmed sets depth @@ -100,11 +99,11 @@ func (set *unconfirmedBlocks) Shift(height uint64) { header := set.chain.GetHeaderByNumber(next.index) switch { case header == nil: - log.Warn(fmt.Sprintf("failed to retrieve header of mined block #%d [%x…]", next.index, next.hash.Bytes()[:4])) + log.Warn("Failed to retrieve header of mined block", "number", next.index, "hash", next.hash) case header.Hash() == next.hash: - log.Info(fmt.Sprintf("🔗 mined block #%d [%x…] reached canonical chain", next.index, next.hash.Bytes()[:4])) + log.Info("🔗 block reached canonical chain", "number", next.index, "hash", next.hash) default: - log.Info(fmt.Sprintf("⑂ mined block #%d [%x…] became a side fork", next.index, next.hash.Bytes()[:4])) + log.Info("⑂ block became a side fork", "number", next.index, "hash", next.hash) } // Drop the block out of the ring if set.blocks.Value == set.blocks.Next().Value { diff --git a/miner/worker.go b/miner/worker.go index 2f090924e..01241b3f3 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -26,6 +26,8 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "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" @@ -34,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "gopkg.in/fatih/set.v0" ) @@ -84,6 +85,7 @@ type Result struct { // worker is the main object which takes care of applying messages to the new state type worker struct { config *params.ChainConfig + engine consensus.Engine mu sync.Mutex @@ -94,7 +96,6 @@ type worker struct { agents map[Agent]struct{} recv chan *Result - pow pow.PoW eth Backend chain *core.BlockChain @@ -123,9 +124,10 @@ type worker struct { fullValidation bool } -func newWorker(config *params.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { +func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { worker := &worker{ config: config, + engine: engine, eth: eth, mux: mux, chainDb: eth.ChainDb(), @@ -209,16 +211,10 @@ func (self *worker) stop() { self.mu.Lock() defer self.mu.Unlock() if atomic.LoadInt32(&self.mining) == 1 { - // Stop all agents. for agent := range self.agents { agent.Stop() - // Remove CPU agents. - if _, ok := agent.(*CpuAgent); ok { - delete(self.agents, agent) - } } } - atomic.StoreInt32(&self.mining, 0) atomic.StoreInt32(&self.atWork, 0) } @@ -256,7 +252,7 @@ func (self *worker) update() { txs := map[common.Address]types.Transactions{acc: {ev.Tx}} txset := types.NewTransactionsByPriceAndNonce(txs) - self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain) + self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain, self.coinbase) self.currentMu.Unlock() } } @@ -277,30 +273,17 @@ func (self *worker) wait() { if self.fullValidation { if _, err := self.chain.InsertChain(types.Blocks{block}); err != nil { - log.Error(fmt.Sprint("mining err", err)) + log.Error("Mined invalid block", "err", err) continue } go self.mux.Post(core.NewMinedBlockEvent{Block: block}) } else { work.state.Commit(self.config.IsEIP158(block.Number())) - parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) - if parent == nil { - log.Error(fmt.Sprint("Invalid block found during mining")) - continue - } - - auxValidator := self.eth.BlockChain().AuxValidator() - if err := core.ValidateHeader(self.config, auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr { - log.Error(fmt.Sprint("Invalid header on mined block:", err)) - continue - } - stat, err := self.chain.WriteBlock(block) if err != nil { - log.Error(fmt.Sprint("error writing block to chain", err)) + log.Error("Failed writing block to chain", "err", err) continue } - // update block hash since it is now available and not when the receipt/log of individual transactions were created for _, r := range work.receipts { for _, l := range r.Logs { @@ -333,7 +316,7 @@ func (self *worker) wait() { self.mux.Post(logs) } if err := core.WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil { - log.Warn(fmt.Sprint("error writing block receipts:", err)) + log.Warn("Failed writing block receipts", "err", err) } }(block, work.state.Logs(), work.receipts) } @@ -424,9 +407,9 @@ func (self *worker) commitNewWork() { tstamp = parent.Time().Int64() + 1 } // this will ensure we're not going off too far in the future - if now := time.Now().Unix(); tstamp > now+4 { + if now := time.Now().Unix(); tstamp > now+1 { wait := time.Duration(tstamp-now) * time.Second - log.Info(fmt.Sprint("We are too far in the future. Waiting for", wait)) + log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait)) time.Sleep(wait) } @@ -434,13 +417,19 @@ func (self *worker) commitNewWork() { header := &types.Header{ ParentHash: parent.Hash(), Number: num.Add(num, common.Big1), - Difficulty: core.CalcDifficulty(self.config, uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()), GasLimit: core.CalcGasLimit(parent), GasUsed: new(big.Int), - Coinbase: self.coinbase, Extra: self.extra, Time: big.NewInt(tstamp), } + // Only set the coinbase if we are mining (avoid spurious block rewards) + if atomic.LoadInt32(&self.mining) == 1 { + header.Coinbase = self.coinbase + } + if err := self.engine.Prepare(self.chain, header); err != nil { + log.Error("Failed to prepare header for mining", "err", err) + return + } // If we are care about TheDAO hard-fork check whether to override the extra-data or not if daoBlock := self.config.DAOForkBlock; daoBlock != nil { // Check whether the block is among the fork extra-override range @@ -457,23 +446,21 @@ func (self *worker) commitNewWork() { // Could potentially happen if starting to mine in an odd state. err := self.makeCurrent(parent, header) if err != nil { - log.Info(fmt.Sprint("Could not create new env for mining, retrying on next block.")) + log.Error("Failed to create mining context", "err", err) return } // Create the current work task and check any fork transitions needed work := self.current if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 { - core.ApplyDAOHardFork(work.state) + misc.ApplyDAOHardFork(work.state) } - pending, err := self.eth.TxPool().Pending() if err != nil { - log.Error(fmt.Sprintf("Could not fetch pending transactions: %v", err)) + log.Error("Failed to fetch pending transactions", "err", err) return } - txs := types.NewTransactionsByPriceAndNonce(pending) - work.commitTransactions(self.mux, txs, self.gasPrice, self.chain) + work.commitTransactions(self.mux, txs, self.gasPrice, self.chain, self.coinbase) self.eth.TxPool().RemoveBatch(work.lowGasTxs) self.eth.TxPool().RemoveBatch(work.failedTxs) @@ -488,31 +475,26 @@ func (self *worker) commitNewWork() { break } if err := self.commitUncle(work, uncle.Header()); err != nil { - log.Trace(fmt.Sprintf("Bad uncle found and will be removed (%x)\n", hash[:4])) + log.Trace("Bad uncle found and will be removed", "hash", hash) log.Trace(fmt.Sprint(uncle)) badUncles = append(badUncles, hash) } else { - log.Debug(fmt.Sprintf("committing %x as uncle\n", hash[:4])) + log.Debug("Committing new uncle to block", "hash", hash) uncles = append(uncles, uncle.Header()) } } for _, hash := range badUncles { delete(self.possibleUncles, hash) } - - if atomic.LoadInt32(&self.mining) == 1 { - // commit state root after all state transitions. - core.AccumulateRewards(work.state, header, uncles) - header.Root = work.state.IntermediateRoot(self.config.IsEIP158(header.Number)) + // Create the new block to seal with the consensus engine + if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil { + log.Error("Failed to finalize block for sealing", "err", err) + return } - - // create the new block whose nonce will be mined. - work.Block = types.NewBlock(header, work.txs, uncles, work.receipts) - // We only care about logging if we're actually mining. if atomic.LoadInt32(&self.mining) == 1 { - log.Info(fmt.Sprintf("commit new work on block %v with %d txs & %d uncles. Took %v\n", work.Block.Number(), work.tcount, len(uncles), time.Since(tstart))) + log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) self.unconfirmed.Shift(work.Block.NumberU64() - 1) } self.push(work) @@ -521,19 +503,19 @@ func (self *worker) commitNewWork() { func (self *worker) commitUncle(work *Work, uncle *types.Header) error { hash := uncle.Hash() if work.uncles.Has(hash) { - return core.UncleError("Uncle not unique") + return fmt.Errorf("uncle not unique") } if !work.ancestors.Has(uncle.ParentHash) { - return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) + return fmt.Errorf("uncle's parent unknown (%x)", uncle.ParentHash[0:4]) } if work.family.Has(hash) { - return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash)) + return fmt.Errorf("uncle already in family (%x)", hash) } work.uncles.Add(uncle.Hash()) return nil } -func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain) { +func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain, coinbase common.Address) { gp := new(core.GasPool).AddGas(env.header.GasLimit) var coalescedLogs []*types.Log @@ -552,7 +534,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB // Check whether the tx is replay protected. If we're not in the EIP155 hf // phase, start ignoring the sender until we do. if tx.Protected() && !env.config.IsEIP155(env.header.Number) { - log.Trace(fmt.Sprintf("Transaction (%x) is replay protected, but we haven't yet hardforked. Transaction will be ignored until we hardfork.\n", tx.Hash())) + log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block) txs.Pop() continue @@ -561,7 +543,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB // Ignore any transactions (and accounts subsequently) with low gas limits if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { // Pop the current low-priced transaction without shifting in the next from the account - log.Info(fmt.Sprintf("Transaction (%x) below gas price (tx=%dwei ask=%dwei). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], tx.GasPrice(), gasPrice, from[:4])) + log.Warn("Transaction below gas price", "sender", from, "hash", tx.Hash(), "have", tx.GasPrice(), "want", gasPrice) env.lowGasTxs = append(env.lowGasTxs, tx) txs.Pop() @@ -571,24 +553,24 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB // Start executing the transaction env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) - err, logs := env.commitTransaction(tx, bc, gp) - switch { - case core.IsGasLimitErr(err): + err, logs := env.commitTransaction(tx, bc, coinbase, gp) + switch err { + case core.ErrGasLimitReached: // Pop the current out-of-gas transaction without shifting in the next from the account - log.Trace(fmt.Sprintf("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4])) + log.Trace("Gas limit exceeded for current block", "sender", from) txs.Pop() - case err != nil: - // Pop the current failed transaction without shifting in the next from the account - log.Trace(fmt.Sprintf("Transaction (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err)) - env.failedTxs = append(env.failedTxs, tx) - txs.Pop() - - default: + case nil: // Everything ok, collect the logs and shift in the next transaction from the same account coalescedLogs = append(coalescedLogs, logs...) env.tcount++ txs.Shift() + + default: + // Pop the current failed transaction without shifting in the next from the account + log.Trace("Transaction failed, will be removed", "hash", tx.Hash(), "err", err) + env.failedTxs = append(env.failedTxs, tx) + txs.Pop() } } @@ -612,10 +594,10 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB } } -func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, []*types.Log) { +func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) { snap := env.state.Snapshot() - receipt, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{}) + receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{}) if err != nil { env.state.RevertToSnapshot(snap) return err, nil diff --git a/mobile/big.go b/mobile/big.go index 9a55836c1..525717caa 100644 --- a/mobile/big.go +++ b/mobile/big.go @@ -93,3 +93,8 @@ func (bi *BigInts) Set(index int, bigint *BigInt) error { bi.bigints[index] = bigint.bigint return nil } + +// GetString returns the value of x as a formatted string in some number base. +func (bi *BigInt) GetString(base int) string { + return bi.bigint.Text(base) +} diff --git a/mobile/context.go b/mobile/context.go index 9df94b689..f1fff9011 100644 --- a/mobile/context.go +++ b/mobile/context.go @@ -20,9 +20,8 @@ package geth import ( + "context" "time" - - "golang.org/x/net/context" ) // Context carries a deadline, a cancelation signal, and other values across API diff --git a/mobile/geth.go b/mobile/geth.go index 62791652d..be04e4603 100644 --- a/mobile/geth.go +++ b/mobile/geth.go @@ -20,15 +20,18 @@ package geth import ( + "encoding/json" "fmt" - "math/big" "path/filepath" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" whisper "github.com/ethereum/go-ethereum/whisper/whisperv2" @@ -53,10 +56,6 @@ type NodeConfig struct { // decide if remote peers should be accepted or not. EthereumNetworkID int - // EthereumChainConfig is the default parameters of the blockchain to use. If no - // configuration is specified, it defaults to the main network. - EthereumChainConfig *ChainConfig - // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An // empty genesis state is equivalent to using the mainnet's state. EthereumGenesis string @@ -82,7 +81,6 @@ var defaultNodeConfig = &NodeConfig{ MaxPeers: 25, EthereumEnabled: true, EthereumNetworkID: 1, - EthereumChainConfig: MainnetChainConfig(), EthereumDatabaseCache: 16, } @@ -111,52 +109,49 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { } // Create the empty networking stack nodeConf := &node.Config{ - Name: clientIdentifier, - Version: params.Version, - DataDir: datadir, - KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! - NoDiscovery: true, - DiscoveryV5: true, - DiscoveryV5Addr: ":0", - BootstrapNodesV5: config.BootstrapNodes.nodes, - ListenAddr: ":0", - NAT: nat.Any(), - MaxPeers: config.MaxPeers, + Name: clientIdentifier, + Version: params.Version, + DataDir: datadir, + KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! + P2P: p2p.Config{ + NoDiscovery: true, + DiscoveryV5: true, + DiscoveryV5Addr: ":0", + BootstrapNodesV5: config.BootstrapNodes.nodes, + ListenAddr: ":0", + NAT: nat.Any(), + MaxPeers: config.MaxPeers, + }, } rawStack, err := node.New(nodeConf) if err != nil { return nil, err } + + var genesis *core.Genesis + if config.EthereumGenesis != "" { + // Parse the user supplied genesis spec if not mainnet + genesis = new(core.Genesis) + if err := json.Unmarshal([]byte(config.EthereumGenesis), genesis); err != nil { + return nil, fmt.Errorf("invalid genesis spec: %v", err) + } + // If we have the testnet, hard code the chain configs too + if config.EthereumGenesis == TestnetGenesis() { + genesis.Config = params.TestnetChainConfig + if config.EthereumNetworkID == 1 { + config.EthereumNetworkID = 3 + } + } + } // Register the Ethereum protocol if requested if config.EthereumEnabled { - ethConf := ð.Config{ - ChainConfig: ¶ms.ChainConfig{ - ChainId: big.NewInt(config.EthereumChainConfig.ChainID), - HomesteadBlock: big.NewInt(config.EthereumChainConfig.HomesteadBlock), - DAOForkBlock: big.NewInt(config.EthereumChainConfig.DAOForkBlock), - DAOForkSupport: config.EthereumChainConfig.DAOForkSupport, - EIP150Block: big.NewInt(config.EthereumChainConfig.EIP150Block), - EIP150Hash: config.EthereumChainConfig.EIP150Hash.hash, - EIP155Block: big.NewInt(config.EthereumChainConfig.EIP155Block), - EIP158Block: big.NewInt(config.EthereumChainConfig.EIP158Block), - }, - Genesis: config.EthereumGenesis, - LightMode: true, - DatabaseCache: config.EthereumDatabaseCache, - NetworkId: config.EthereumNetworkID, - GasPrice: new(big.Int).SetUint64(20 * params.Shannon), - GpoMinGasPrice: new(big.Int).SetUint64(50 * params.Shannon), - GpoMaxGasPrice: new(big.Int).SetUint64(500 * params.Shannon), - GpoFullBlockRatio: 80, - GpobaseStepDown: 10, - GpobaseStepUp: 100, - GpobaseCorrectionFactor: 110, - EthashCacheDir: "ethash", - EthashCachesInMem: 2, - EthashCachesOnDisk: 3, - } + ethConf := eth.DefaultConfig + ethConf.Genesis = genesis + ethConf.SyncMode = downloader.LightSync + ethConf.NetworkId = config.EthereumNetworkID + ethConf.DatabaseCache = config.EthereumDatabaseCache if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return les.New(ctx, ethConf) + return les.New(ctx, ðConf) }); err != nil { return nil, fmt.Errorf("ethereum init: %v", err) } diff --git a/mobile/params.go b/mobile/params.go index 87747c7b0..9c58a90ab 100644 --- a/mobile/params.go +++ b/mobile/params.go @@ -19,66 +19,26 @@ package geth import ( + "encoding/json" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/params" ) -// MainnetChainConfig returns the chain configurations for the main Ethereum network. -func MainnetChainConfig() *ChainConfig { - return &ChainConfig{ - ChainID: params.MainNetChainID.Int64(), - HomesteadBlock: params.MainNetHomesteadBlock.Int64(), - DAOForkBlock: params.MainNetDAOForkBlock.Int64(), - DAOForkSupport: true, - EIP150Block: params.MainNetHomesteadGasRepriceBlock.Int64(), - EIP150Hash: Hash{params.MainNetHomesteadGasRepriceHash}, - EIP155Block: params.MainNetSpuriousDragon.Int64(), - EIP158Block: params.MainNetSpuriousDragon.Int64(), - } -} - // MainnetGenesis returns the JSON spec to use for the main Ethereum network. It // is actually empty since that defaults to the hard coded binary genesis block. func MainnetGenesis() string { return "" } -// TestnetChainConfig returns the chain configurations for the Ethereum test network. -func TestnetChainConfig() *ChainConfig { - return &ChainConfig{ - ChainID: params.TestNetChainID.Int64(), - HomesteadBlock: params.TestNetHomesteadBlock.Int64(), - DAOForkBlock: 0, - DAOForkSupport: false, - EIP150Block: params.TestNetHomesteadGasRepriceBlock.Int64(), - EIP150Hash: Hash{params.TestNetHomesteadGasRepriceHash}, - EIP155Block: params.TestNetSpuriousDragon.Int64(), - EIP158Block: params.TestNetSpuriousDragon.Int64(), - } -} - // TestnetGenesis returns the JSON spec to use for the Ethereum test network. func TestnetGenesis() string { - return core.DefaultTestnetGenesisBlock() -} - -// ChainConfig is the core config which determines the blockchain settings. -type ChainConfig struct { - ChainID int64 // Chain ID for replay protection - HomesteadBlock int64 // Homestead switch block - DAOForkBlock int64 // TheDAO hard-fork switch block - DAOForkSupport bool // Whether the nodes supports or opposes the DAO hard-fork - EIP150Block int64 // Homestead gas reprice switch block - EIP150Hash Hash // Homestead gas reprice switch block hash - EIP155Block int64 // Replay protection switch block - EIP158Block int64 // Empty account pruning switch block -} - -// NewChainConfig creates a new chain configuration that transitions immediately -// to homestead and has no notion of the DAO fork (ideal for a private network). -func NewChainConfig() *ChainConfig { - return new(ChainConfig) + enc, err := json.Marshal(core.DefaultTestnetGenesisBlock()) + if err != nil { + panic(err) + } + return string(enc) } // FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated diff --git a/node/api.go b/node/api.go index 3c451fc8a..570cb9d98 100644 --- a/node/api.go +++ b/node/api.go @@ -92,8 +92,13 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis if port == nil { port = &api.node.config.HTTPPort } - if cors == nil { - cors = &api.node.config.HTTPCors + + allowedOrigins := api.node.config.HTTPCors + if cors != nil { + allowedOrigins = nil + for _, origin := range strings.Split(*cors, ",") { + allowedOrigins = append(allowedOrigins, strings.TrimSpace(origin)) + } } modules := api.node.httpWhitelist @@ -104,7 +109,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis } } - if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, *cors); err != nil { + if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins); err != nil { return false, err } return true, nil @@ -141,8 +146,13 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str if port == nil { port = &api.node.config.WSPort } - if allowedOrigins == nil { - allowedOrigins = &api.node.config.WSOrigins + + origins := api.node.config.WSOrigins + if allowedOrigins != nil { + origins = nil + for _, origin := range strings.Split(*allowedOrigins, ",") { + origins = append(origins, strings.TrimSpace(origin)) + } } modules := api.node.config.WSModules @@ -153,7 +163,7 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str } } - if err := api.node.startWS(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, *allowedOrigins); err != nil { + if err := api.node.startWS(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, origins); err != nil { return false, err } return true, nil diff --git a/node/config.go b/node/config.go index 608c9a6b4..1bab4c574 100644 --- a/node/config.go +++ b/node/config.go @@ -20,7 +20,6 @@ import ( "crypto/ecdsa" "fmt" "io/ioutil" - "net" "os" "path/filepath" "runtime" @@ -32,10 +31,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/ethereum/go-ethereum/p2p/nat" - "github.com/ethereum/go-ethereum/p2p/netutil" ) var ( @@ -53,14 +50,14 @@ type Config struct { // Name sets the instance name of the node. It must not contain the / character and is // used in the devp2p node identifier. The instance name of geth is "geth". If no // value is specified, the basename of the current executable is used. - Name string + Name string `toml:"-"` // UserIdent, if set, is used as an additional component in the devp2p node identifier. - UserIdent string + UserIdent string `toml:",omitempty"` // Version should be set to the version number of the program. It is used // in the devp2p node identifier. - Version string + Version string `toml:"-"` // DataDir is the file system folder the node should use for any data storage // requirements. The configured data directory will not be directly shared with @@ -69,6 +66,9 @@ type Config struct { // in memory. DataDir string + // Configuration of peer-to-peer networking. + P2P p2p.Config + // KeyStoreDir is the file system folder that contains private keys. The directory can // be specified as a relative path, in which case it is resolved relative to the // current directory. @@ -76,106 +76,55 @@ type Config struct { // If KeyStoreDir is empty, the default location is the "keystore" subdirectory of // DataDir. If DataDir is unspecified and KeyStoreDir is empty, an ephemeral directory // is created by New and destroyed when the node is stopped. - KeyStoreDir string + KeyStoreDir string `toml:",omitempty"` // UseLightweightKDF lowers the memory and CPU requirements of the key store // scrypt KDF at the expense of security. - UseLightweightKDF bool + UseLightweightKDF bool `toml:",omitempty"` // IPCPath is the requested location to place the IPC endpoint. If the path is // a simple file name, it is placed inside the data directory (or on the root // pipe path on Windows), whereas if it's a resolvable path name (absolute or // relative), then that specific path is enforced. An empty path disables IPC. - IPCPath string - - // This field should be a valid secp256k1 private key that will be used for both - // remote peer identification as well as network traffic encryption. If no key - // is configured, the preset one is loaded from the data dir, generating it if - // needed. - PrivateKey *ecdsa.PrivateKey - - // NoDiscovery specifies whether the peer discovery mechanism should be started - // or not. Disabling is usually useful for protocol debugging (manual topology). - NoDiscovery bool - - // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery - // protocol should be started or not. - DiscoveryV5 bool - - // Listener address for the V5 discovery protocol UDP traffic. - DiscoveryV5Addr string - - // Restrict communication to white listed IP networks. - // The whitelist only applies when non-nil. - NetRestrict *netutil.Netlist - - // BootstrapNodes used to establish connectivity with the rest of the network. - BootstrapNodes []*discover.Node - - // BootstrapNodesV5 used to establish connectivity with the rest of the network - // using the V5 discovery protocol. - BootstrapNodesV5 []*discv5.Node - - // Network interface address on which the node should listen for inbound peers. - ListenAddr string - - // If set to a non-nil value, the given NAT port mapper is used to make the - // listening port available to the Internet. - NAT nat.Interface - - // If Dialer is set to a non-nil value, the given Dialer is used to dial outbound - // peer connections. - Dialer *net.Dialer - - // If NoDial is true, the node will not dial any peers. - NoDial bool - - // MaxPeers is the maximum number of peers that can be connected. If this is - // set to zero, then only the configured static and trusted peers can connect. - MaxPeers int - - // MaxPendingPeers is the maximum number of peers that can be pending in the - // handshake phase, counted separately for inbound and outbound connections. - // Zero defaults to preset values. - MaxPendingPeers int + IPCPath string `toml:",omitempty"` // HTTPHost is the host interface on which to start the HTTP RPC server. If this // field is empty, no HTTP API endpoint will be started. - HTTPHost string + HTTPHost string `toml:",omitempty"` // HTTPPort is the TCP port number on which to start the HTTP RPC server. The // default zero value is/ valid and will pick a port number randomly (useful // for ephemeral nodes). - HTTPPort int + HTTPPort int `toml:",omitempty"` // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting // clients. Please be aware that CORS is a browser enforced security, it's fully // useless for custom HTTP clients. - HTTPCors string + HTTPCors []string `toml:",omitempty"` // HTTPModules is a list of API modules to expose via the HTTP RPC interface. // If the module list is empty, all RPC API endpoints designated public will be // exposed. - HTTPModules []string + HTTPModules []string `toml:",omitempty"` // WSHost is the host interface on which to start the websocket RPC server. If // this field is empty, no websocket API endpoint will be started. - WSHost string + WSHost string `toml:",omitempty"` // WSPort is the TCP port number on which to start the websocket RPC server. The // default zero value is/ valid and will pick a port number randomly (useful for // ephemeral nodes). - WSPort int + WSPort int `toml:",omitempty"` // WSOrigins is the list of domain to accept websocket requests from. Please be // aware that the server can only act upon the HTTP request the client sends and // cannot verify the validity of the request header. - WSOrigins string + WSOrigins []string `toml:",omitempty"` // WSModules is a list of API modules to expose via the websocket RPC interface. // If the module list is empty, all RPC API endpoints designated public will be // exposed. - WSModules []string + WSModules []string `toml:",omitempty"` } // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into @@ -266,7 +215,7 @@ func (c *Config) NodeName() string { if c.Version != "" { name += "/v" + c.Version } - name += "/" + runtime.GOOS + name += "/" + runtime.GOOS + "-" + runtime.GOARCH name += "/" + runtime.Version() return name } @@ -326,8 +275,8 @@ func (c *Config) instanceDir() string { // data folder. If no key can be found, a new one is generated. func (c *Config) NodeKey() *ecdsa.PrivateKey { // Use any specifically configured key. - if c.PrivateKey != nil { - return c.PrivateKey + if c.P2P.PrivateKey != nil { + return c.P2P.PrivateKey } // Generate ephemeral key if no datadir is being used. if c.DataDir == "" { diff --git a/node/config_test.go b/node/config_test.go index c0eda72c2..b81d3d612 100644 --- a/node/config_test.go +++ b/node/config_test.go @@ -25,6 +25,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" ) // Tests that datadirs can be successfully created, be them manually configured @@ -109,7 +110,7 @@ func TestNodeKeyPersistency(t *testing.T) { if err != nil { t.Fatalf("failed to generate one-shot node key: %v", err) } - config := &Config{Name: "unit-test", DataDir: dir, PrivateKey: key} + config := &Config{Name: "unit-test", DataDir: dir, P2P: p2p.Config{PrivateKey: key}} config.NodeKey() if _, err := os.Stat(filepath.Join(keyfile)); err == nil { t.Fatalf("one-shot node key persisted to data directory") diff --git a/node/defaults.go b/node/defaults.go index bfe257c8e..d4e148683 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -21,16 +21,32 @@ import ( "os/user" "path/filepath" "runtime" + + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/nat" ) const ( - DefaultIPCSocket = "geth.ipc" // Default (relative) name of the IPC RPC socket - DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server - DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server - DefaultWSHost = "localhost" // Default host interface for the websocket RPC server - DefaultWSPort = 8546 // Default TCP port for the websocket RPC server + DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server + DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server + DefaultWSHost = "localhost" // Default host interface for the websocket RPC server + DefaultWSPort = 8546 // Default TCP port for the websocket RPC server ) +// DefaultConfig contains reasonable default settings. +var DefaultConfig = Config{ + DataDir: DefaultDataDir(), + HTTPPort: DefaultHTTPPort, + HTTPModules: []string{"net", "web3"}, + WSPort: DefaultWSPort, + WSModules: []string{"net", "web3"}, + P2P: p2p.Config{ + ListenAddr: ":30303", + MaxPeers: 25, + NAT: nat.Any(), + }, +} + // DefaultDataDir is the default data directory to use for the databases and other // persistence requirements. func DefaultDataDir() string { diff --git a/node/node.go b/node/node.go index c7e28af37..dc2ff0701 100644 --- a/node/node.go +++ b/node/node.go @@ -153,27 +153,20 @@ func (n *Node) Start() error { // Initialize the p2p server. This creates the node key and // discovery databases. - n.serverConfig = p2p.Config{ - PrivateKey: n.config.NodeKey(), - Name: n.config.NodeName(), - Discovery: !n.config.NoDiscovery, - DiscoveryV5: n.config.DiscoveryV5, - DiscoveryV5Addr: n.config.DiscoveryV5Addr, - BootstrapNodes: n.config.BootstrapNodes, - BootstrapNodesV5: n.config.BootstrapNodesV5, - StaticNodes: n.config.StaticNodes(), - TrustedNodes: n.config.TrusterNodes(), - NodeDatabase: n.config.NodeDB(), - ListenAddr: n.config.ListenAddr, - NetRestrict: n.config.NetRestrict, - NAT: n.config.NAT, - Dialer: n.config.Dialer, - NoDial: n.config.NoDial, - MaxPeers: n.config.MaxPeers, - MaxPendingPeers: n.config.MaxPendingPeers, + n.serverConfig = n.config.P2P + n.serverConfig.PrivateKey = n.config.NodeKey() + n.serverConfig.Name = n.config.NodeName() + if n.serverConfig.StaticNodes == nil { + n.serverConfig.StaticNodes = n.config.StaticNodes() + } + if n.serverConfig.TrustedNodes == nil { + n.serverConfig.TrustedNodes = n.config.TrusterNodes() + } + if n.serverConfig.NodeDatabase == "" { + n.serverConfig.NodeDatabase = n.config.NodeDB() } running := &p2p.Server{Config: n.serverConfig} - log.Info(fmt.Sprint("instance:", n.serverConfig.Name)) + log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name) // Otherwise copy and specialize the P2P configuration services := make(map[reflect.Type]Service) @@ -379,7 +372,7 @@ func (n *Node) stopIPC() { } // startHTTP initializes and starts the HTTP RPC endpoint. -func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors string) error { +func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string) error { // Short circuit if the HTTP endpoint isn't being exposed if endpoint == "" { return nil @@ -433,7 +426,7 @@ func (n *Node) stopHTTP() { } // startWS initializes and starts the websocket RPC endpoint. -func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins string) error { +func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string) error { // Short circuit if the WS endpoint isn't being exposed if endpoint == "" { return nil diff --git a/node/node_example_test.go b/node/node_example_test.go index d2872cf38..ee06f4065 100644 --- a/node/node_example_test.go +++ b/node/node_example_test.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/rpc" ) @@ -42,23 +41,8 @@ func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starti func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil } func ExampleService() { - // Create a network node to run protocols with the default values. The below list - // is only used to display each of the configuration options. All of these could - // have been omitted if the default behavior is desired. - nodeConfig := &node.Config{ - DataDir: "", // Empty uses ephemeral storage - PrivateKey: nil, // Nil generates a node key on the fly - Name: "", // Any textual node name is allowed - NoDiscovery: false, // Can disable discovering remote nodes - BootstrapNodes: []*discover.Node{}, // List of bootstrap nodes to use - ListenAddr: ":0", // Network interface to listen on - NAT: nil, // UPnP port mapper to use for crossing firewalls - Dialer: nil, // Custom dialer to use for establishing peer connections - NoDial: false, // Can prevent this node from dialing out - MaxPeers: 0, // Number of peers to allow - MaxPendingPeers: 0, // Number of peers allowed to handshake concurrently - } - stack, err := node.New(nodeConfig) + // Create a network node to run protocols with the default values. + stack, err := node.New(&node.Config{}) if err != nil { log.Fatalf("Failed to create network node: %v", err) } diff --git a/node/node_test.go b/node/node_test.go index 408d4cfcb..2880efa61 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -35,8 +35,8 @@ var ( func testNodeConfig() *Config { return &Config{ - PrivateKey: testNodeKey, - Name: "test node", + Name: "test node", + P2P: p2p.Config{PrivateKey: testNodeKey}, } } diff --git a/p2p/dial.go b/p2p/dial.go index bb3befab2..b77971396 100644 --- a/p2p/dial.go +++ b/p2p/dial.go @@ -38,6 +38,10 @@ const ( // once every few seconds. lookupInterval = 4 * time.Second + // If no peers are found for this amount of time, the initial bootnodes are + // attempted to be connected. + fallbackInterval = 20 * time.Second + // Endpoint resolution is throttled with bounded backoff. initialResolveDelay = 60 * time.Second maxResolveDelay = time.Hour @@ -57,6 +61,9 @@ type dialstate struct { randomNodes []*discover.Node // filled from Table static map[discover.NodeID]*dialTask hist *dialHistory + + start time.Time // time when the dialer was first used + bootnodes []*discover.Node // default dials when there are no peers } type discoverTable interface { @@ -102,16 +109,18 @@ type waitExpireTask struct { time.Duration } -func newDialState(static []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate { +func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate { s := &dialstate{ maxDynDials: maxdyn, ntab: ntab, netrestrict: netrestrict, static: make(map[discover.NodeID]*dialTask), dialing: make(map[discover.NodeID]connFlag), + bootnodes: make([]*discover.Node, len(bootnodes)), randomNodes: make([]*discover.Node, maxdyn/2), hist: new(dialHistory), } + copy(s.bootnodes, bootnodes) for _, n := range static { s.addStatic(n) } @@ -130,6 +139,10 @@ func (s *dialstate) removeStatic(n *discover.Node) { } func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task { + if s.start == (time.Time{}) { + s.start = now + } + var newtasks []task addDial := func(flag connFlag, n *discover.Node) bool { if err := s.checkDial(n, peers); err != nil { @@ -169,7 +182,18 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now newtasks = append(newtasks, t) } } - + // If we don't have any peers whatsoever, try to dial a random bootnode. This + // scenario is useful for the testnet (and private networks) where the discovery + // table might be full of mostly bad peers, making it hard to find good ones. + if len(peers) == 0 && len(s.bootnodes) > 0 && needDynDials > 0 && now.Sub(s.start) > fallbackInterval { + bootnode := s.bootnodes[0] + s.bootnodes = append(s.bootnodes[:0], s.bootnodes[1:]...) + s.bootnodes = append(s.bootnodes, bootnode) + + if addDial(dynDialedConn, bootnode) { + needDynDials-- + } + } // Use random nodes from the table for half of the necessary // dynamic dials. randomCandidates := needDynDials / 2 diff --git a/p2p/dial_test.go b/p2p/dial_test.go index c850233db..08e863bae 100644 --- a/p2p/dial_test.go +++ b/p2p/dial_test.go @@ -87,7 +87,7 @@ func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, // This test checks that dynamic dials are launched from discovery results. func TestDialStateDynDial(t *testing.T) { runDialTest(t, dialtest{ - init: newDialState(nil, fakeTable{}, 5, nil), + init: newDialState(nil, nil, fakeTable{}, 5, nil), rounds: []round{ // A discovery query is launched. { @@ -219,6 +219,94 @@ func TestDialStateDynDial(t *testing.T) { }) } +// Tests that bootnodes are dialed if no peers are connectd, but not otherwise. +func TestDialStateDynDialBootnode(t *testing.T) { + bootnodes := []*discover.Node{ + {ID: uintID(1)}, + {ID: uintID(2)}, + {ID: uintID(3)}, + } + table := fakeTable{ + {ID: uintID(4)}, + {ID: uintID(5)}, + {ID: uintID(6)}, + {ID: uintID(7)}, + {ID: uintID(8)}, + } + runDialTest(t, dialtest{ + init: newDialState(nil, bootnodes, table, 5, nil), + rounds: []round{ + // 2 dynamic dials attempted, bootnodes pending fallback interval + { + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + &discoverTask{}, + }, + }, + // No dials succeed, bootnodes still pending fallback interval + { + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + }, + // No dials succeed, bootnodes still pending fallback interval + {}, + // No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached + { + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + }, + // No dials succeed, 2nd bootnode is attempted + { + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, + }, + }, + // No dials succeed, 3rd bootnode is attempted + { + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, + }, + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, + }, + }, + // No dials succeed, 1st bootnode is attempted again, expired random nodes retried + { + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, + }, + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + }, + // Random dial succeeds, no more bootnodes are attempted + { + peers: []*Peer{ + {rw: &conn{flags: dynDialedConn, id: uintID(4)}}, + }, + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + }, + }, + }) +} + func TestDialStateDynDialFromTable(t *testing.T) { // This table always returns the same random nodes // in the order given below. @@ -234,7 +322,7 @@ func TestDialStateDynDialFromTable(t *testing.T) { } runDialTest(t, dialtest{ - init: newDialState(nil, table, 10, nil), + init: newDialState(nil, nil, table, 10, nil), rounds: []round{ // 5 out of 8 of the nodes returned by ReadRandomNodes are dialed. { @@ -332,7 +420,7 @@ func TestDialStateNetRestrict(t *testing.T) { restrict.Add("127.0.2.0/24") runDialTest(t, dialtest{ - init: newDialState(nil, table, 10, restrict), + init: newDialState(nil, nil, table, 10, restrict), rounds: []round{ { new: []task{ @@ -355,7 +443,7 @@ func TestDialStateStaticDial(t *testing.T) { } runDialTest(t, dialtest{ - init: newDialState(wantStatic, fakeTable{}, 0, nil), + init: newDialState(wantStatic, nil, fakeTable{}, 0, nil), rounds: []round{ // Static dials are launched for the nodes that // aren't yet connected. @@ -436,7 +524,7 @@ func TestDialStateCache(t *testing.T) { } runDialTest(t, dialtest{ - init: newDialState(wantStatic, fakeTable{}, 0, nil), + init: newDialState(wantStatic, nil, fakeTable{}, 0, nil), rounds: []round{ // Static dials are launched for the nodes that // aren't yet connected. @@ -498,7 +586,7 @@ func TestDialStateCache(t *testing.T) { func TestDialResolve(t *testing.T) { resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444) table := &resolveMock{answer: resolved} - state := newDialState(nil, table, 0, nil) + state := newDialState(nil, nil, table, 0, nil) // Check that the task is generated with an incomplete ID. dest := discover.NewNode(uintID(1), nil, 0, 0) diff --git a/p2p/discover/node.go b/p2p/discover/node.go index 6a7ab814e..d9cbd9448 100644 --- a/p2p/discover/node.go +++ b/p2p/discover/node.go @@ -207,6 +207,20 @@ func MustParseNode(rawurl string) *Node { return n } +// MarshalText implements encoding.TextMarshaler. +func (n *Node) MarshalText() ([]byte, error) { + return []byte(n.String()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (n *Node) UnmarshalText(text []byte) error { + dec, err := ParseNode(string(text)) + if err == nil { + *n = *dec + } + return err +} + // NodeID is a unique identifier for each node. // The node identifier is a marshaled elliptic curve public key. type NodeID [NodeIDBits / 8]byte diff --git a/p2p/discv5/node.go b/p2p/discv5/node.go index c99b4da14..2db7a508f 100644 --- a/p2p/discv5/node.go +++ b/p2p/discv5/node.go @@ -215,6 +215,20 @@ func MustParseNode(rawurl string) *Node { return n } +// MarshalText implements encoding.TextMarshaler. +func (n *Node) MarshalText() ([]byte, error) { + return []byte(n.String()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (n *Node) UnmarshalText(text []byte) error { + dec, err := ParseNode(string(text)) + if err == nil { + *n = *dec + } + return err +} + // type nodeQueue []*Node // // // pushNew adds n to the end if it is not present. diff --git a/p2p/netutil/net.go b/p2p/netutil/net.go index 3c3715788..f6005afd2 100644 --- a/p2p/netutil/net.go +++ b/p2p/netutil/net.go @@ -84,6 +84,31 @@ func ParseNetlist(s string) (*Netlist, error) { return &l, nil } +// MarshalTOML implements toml.MarshalerRec. +func (l Netlist) MarshalTOML() interface{} { + list := make([]string, 0, len(l)) + for _, net := range l { + list = append(list, net.String()) + } + return list +} + +// UnmarshalTOML implements toml.UnmarshalerRec. +func (l *Netlist) UnmarshalTOML(fn func(interface{}) error) error { + var masks []string + if err := fn(&masks); err != nil { + return err + } + for _, mask := range masks { + _, n, err := net.ParseCIDR(mask) + if err != nil { + return err + } + *l = append(*l, *n) + } + return nil +} + // Add parses a CIDR mask and appends it to the list. It panics for invalid masks and is // intended to be used for setting up static lists. func (l *Netlist) Add(cidr string) { diff --git a/p2p/server.go b/p2p/server.go index 48b4e8be3..d7909d53a 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -58,7 +58,7 @@ var errServerStopped = errors.New("server stopped") // Config holds Server options. type Config struct { // This field must be set to a valid secp256k1 private key. - PrivateKey *ecdsa.PrivateKey + PrivateKey *ecdsa.PrivateKey `toml:"-"` // MaxPeers is the maximum number of peers that can be // connected. It must be greater than zero. @@ -67,22 +67,22 @@ type Config struct { // MaxPendingPeers is the maximum number of peers that can be pending in the // handshake phase, counted separately for inbound and outbound connections. // Zero defaults to preset values. - MaxPendingPeers int + MaxPendingPeers int `toml:",omitempty"` - // Discovery specifies whether the peer discovery mechanism should be started - // or not. Disabling is usually useful for protocol debugging (manual topology). - Discovery bool + // NoDiscovery can be used to disable the peer discovery mechanism. + // Disabling is useful for protocol debugging (manual topology). + NoDiscovery bool // DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery // protocol should be started or not. - DiscoveryV5 bool + DiscoveryV5 bool `toml:",omitempty"` // Listener address for the V5 discovery protocol UDP traffic. - DiscoveryV5Addr string + DiscoveryV5Addr string `toml:",omitempty"` // Name sets the node name of this server. // Use common.MakeName to create a name that follows existing conventions. - Name string + Name string `toml:"-"` // BootstrapNodes are used to establish connectivity // with the rest of the network. @@ -91,7 +91,7 @@ type Config struct { // BootstrapNodesV5 are used to establish connectivity // with the rest of the network using the V5 discovery // protocol. - BootstrapNodesV5 []*discv5.Node + BootstrapNodesV5 []*discv5.Node `toml:",omitempty"` // Static nodes are used as pre-configured connections which are always // maintained and re-connected on disconnects. @@ -104,16 +104,16 @@ type Config struct { // Connectivity can be restricted to certain IP networks. // If this option is set to a non-nil value, only hosts which match one of the // IP networks contained in the list are considered. - NetRestrict *netutil.Netlist + NetRestrict *netutil.Netlist `toml:",omitempty"` // NodeDatabase is the path to the database containing the previously seen // live nodes in the network. - NodeDatabase string + NodeDatabase string `toml:",omitempty"` // Protocols should contain the protocols supported // by the server. Matching protocols are launched for // each peer. - Protocols []Protocol + Protocols []Protocol `toml:"-"` // If ListenAddr is set to a non-nil address, the server // will listen for incoming connections. @@ -126,14 +126,14 @@ type Config struct { // If set to a non-nil value, the given NAT port mapper // is used to make the listening port available to the // Internet. - NAT nat.Interface + NAT nat.Interface `toml:",omitempty"` // If Dialer is set to a non-nil value, the given Dialer // is used to dial outbound peer connections. - Dialer *net.Dialer + Dialer *net.Dialer `toml:"-"` // If NoDial is true, the server will not dial any peers. - NoDial bool + NoDial bool `toml:",omitempty"` } // Server manages all peer connections. @@ -370,7 +370,7 @@ func (srv *Server) Start() (err error) { srv.peerOpDone = make(chan struct{}) // node table - if srv.Discovery { + if !srv.NoDiscovery { ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase, srv.NetRestrict) if err != nil { return err @@ -393,10 +393,10 @@ func (srv *Server) Start() (err error) { } dynPeers := (srv.MaxPeers + 1) / 2 - if !srv.Discovery { + if srv.NoDiscovery { dynPeers = 0 } - dialer := newDialState(srv.StaticNodes, srv.ntab, dynPeers, srv.NetRestrict) + dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict) // handshake srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)} diff --git a/params/bootnodes.go b/params/bootnodes.go index ab7ec4659..496ab68ec 100644 --- a/params/bootnodes.go +++ b/params/bootnodes.go @@ -33,10 +33,10 @@ var MainnetBootnodes = []string{ } // TestnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the -// Morden test network. +// Ropsten test network. var TestnetBootnodes = []string{ - "enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404", - "enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303", + "enode://6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d@13.84.180.240:30303", // US-TX + "enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303", // IE } // DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the diff --git a/params/config.go b/params/config.go index ee993fe4a..c5a2afb5b 100644 --- a/params/config.go +++ b/params/config.go @@ -23,39 +23,45 @@ import ( "github.com/ethereum/go-ethereum/common" ) -// MainnetChainConfig is the chain parameters to run a node on the main network. -var MainnetChainConfig = &ChainConfig{ - ChainId: MainNetChainID, - HomesteadBlock: MainNetHomesteadBlock, - DAOForkBlock: MainNetDAOForkBlock, - DAOForkSupport: true, - EIP150Block: MainNetHomesteadGasRepriceBlock, - EIP150Hash: MainNetHomesteadGasRepriceHash, - EIP155Block: MainNetSpuriousDragon, - EIP158Block: MainNetSpuriousDragon, -} - -// TestnetChainConfig is the chain parameters to run a node on the test network. -var TestnetChainConfig = &ChainConfig{ - ChainId: big.NewInt(3), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: nil, - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), - EIP155Block: big.NewInt(10), - EIP158Block: big.NewInt(10), -} - -// AllProtocolChanges contains every protocol change (EIPs) -// introduced and accepted by the Ethereum core developers. -// -// This configuration is intentionally not using keyed fields. -// This configuration must *always* have all forks enabled, which -// means that all fields must be set at all times. This forces -// anyone adding flags to the config to also have to set these -// fields. -var AllProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0)} +var ( + // MainnetChainConfig is the chain parameters to run a node on the main network. + MainnetChainConfig = &ChainConfig{ + ChainId: MainNetChainID, + HomesteadBlock: MainNetHomesteadBlock, + DAOForkBlock: MainNetDAOForkBlock, + DAOForkSupport: true, + EIP150Block: MainNetHomesteadGasRepriceBlock, + EIP150Hash: MainNetHomesteadGasRepriceHash, + EIP155Block: MainNetSpuriousDragon, + EIP158Block: MainNetSpuriousDragon, + Ethash: new(EthashConfig), + } + + // TestnetChainConfig contains the chain parameters to run a node on the ropsten test network. + TestnetChainConfig = &ChainConfig{ + ChainId: big.NewInt(3), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), + EIP155Block: big.NewInt(10), + EIP158Block: big.NewInt(10), + Ethash: new(EthashConfig), + } + + // AllProtocolChanges contains every protocol change (EIPs) + // introduced and accepted by the Ethereum core developers. + // TestChainConfig is like AllProtocolChanges but has chain ID 1. + // + // This configuration is intentionally not using keyed fields. + // This configuration must *always* have all forks enabled, which + // means that all fields must be set at all times. This forces + // anyone adding flags to the config to also have to set these + // fields. + AllProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), new(EthashConfig), nil} +) // ChainConfig is the core config which determines the blockchain settings. // @@ -65,21 +71,53 @@ var AllProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, fals type ChainConfig struct { ChainId *big.Int `json:"chainId"` // Chain id identifies the current chain and is used for replay protection - HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead) - DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) - DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork + HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead) + DAOForkBlock *big.Int `json:"daoForkBlock,omitempty"` // TheDAO hard-fork switch block (nil = no fork) + DAOForkSupport bool `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) - EIP150Block *big.Int `json:"eip150Block"` // EIP150 HF block (nil = no fork) - EIP150Hash common.Hash `json:"eip150Hash"` // EIP150 HF hash (fast sync aid) + EIP150Block *big.Int `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork) + EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (fast sync aid) + + EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block + EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block + + // Various consensus engines + Ethash *EthashConfig `json:"ethash,omitempty"` + Clique *CliqueConfig `json:"clique,omitempty"` +} + +// EthashConfig is the consensus engine configs for proof-of-work based sealing. +type EthashConfig struct{} + +// String implements the stringer interface, returning the consensus engine details. +func (c *EthashConfig) String() string { + return "ethash" +} + +// CliqueConfig is the consensus engine configs for proof-of-authority based sealing. +type CliqueConfig struct { + Period uint64 `json:"period"` // Number of seconds between blocks to enforce + Epoch uint64 `json:"epoch"` // Epoch length to reset votes and checkpoint +} - EIP155Block *big.Int `json:"eip155Block"` // EIP155 HF block - EIP158Block *big.Int `json:"eip158Block"` // EIP158 HF block +// String implements the stringer interface, returning the consensus engine details. +func (c *CliqueConfig) String() string { + return "clique" } -// String implements the Stringer interface. +// String implements the fmt.Stringer interface. func (c *ChainConfig) String() string { - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v}", + var engine interface{} + switch { + case c.Ethash != nil: + engine = c.Ethash + case c.Clique != nil: + engine = c.Clique + default: + engine = "unknown" + } + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Engine: %v}", c.ChainId, c.HomesteadBlock, c.DAOForkBlock, @@ -87,20 +125,30 @@ func (c *ChainConfig) String() string { c.EIP150Block, c.EIP155Block, c.EIP158Block, + engine, ) } -var ( - TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)} - TestRules = TestChainConfig.Rules(new(big.Int)) -) - // IsHomestead returns whether num is either equal to the homestead block or greater. func (c *ChainConfig) IsHomestead(num *big.Int) bool { - if c.HomesteadBlock == nil || num == nil { - return false - } - return num.Cmp(c.HomesteadBlock) >= 0 + return isForked(c.HomesteadBlock, num) +} + +// IsDAO returns whether num is either equal to the DAO fork block or greater. +func (c *ChainConfig) IsDAOFork(num *big.Int) bool { + return isForked(c.DAOForkBlock, num) +} + +func (c *ChainConfig) IsEIP150(num *big.Int) bool { + return isForked(c.EIP150Block, num) +} + +func (c *ChainConfig) IsEIP155(num *big.Int) bool { + return isForked(c.EIP155Block, num) +} + +func (c *ChainConfig) IsEIP158(num *big.Int) bool { + return isForked(c.EIP158Block, num) } // GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). @@ -110,51 +158,110 @@ func (c *ChainConfig) GasTable(num *big.Int) GasTable { if num == nil { return GasTableHomestead } - switch { - case c.EIP158Block != nil && num.Cmp(c.EIP158Block) >= 0: + case c.IsEIP158(num): return GasTableEIP158 - case c.EIP150Block != nil && num.Cmp(c.EIP150Block) >= 0: + case c.IsEIP150(num): return GasTableHomesteadGasRepriceFork default: return GasTableHomestead } } -func (c *ChainConfig) IsEIP150(num *big.Int) bool { - if c.EIP150Block == nil || num == nil { - return false - } - return num.Cmp(c.EIP150Block) >= 0 +// CheckCompatible checks whether scheduled fork transitions have been imported +// with a mismatching chain configuration. +func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError { + bhead := new(big.Int).SetUint64(height) + // Iterate checkCompatible to find the lowest conflict. + var lasterr *ConfigCompatError + for { + err := c.checkCompatible(newcfg, bhead) + if err == nil || (lasterr != nil && err.RewindTo == lasterr.RewindTo) { + break + } + lasterr = err + bhead.SetUint64(err.RewindTo) + } + return lasterr } -func (c *ChainConfig) IsEIP155(num *big.Int) bool { - if c.EIP155Block == nil || num == nil { - return false +func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError { + if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) { + return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock) + } + if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, head) { + return newCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock) + } + if c.IsDAOFork(head) && c.DAOForkSupport != newcfg.DAOForkSupport { + return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock) + } + if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, head) { + return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block) + } + if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) { + return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block) } - return num.Cmp(c.EIP155Block) >= 0 + if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) { + return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block) + } + if c.IsEIP158(head) && !configNumEqual(c.ChainId, newcfg.ChainId) { + return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block) + } + return nil +} +// isForkIncompatible returns true if a fork scheduled at s1 cannot be rescheduled to +// block s2 because head is already past the fork. +func isForkIncompatible(s1, s2, head *big.Int) bool { + return (isForked(s1, head) || isForked(s2, head)) && !configNumEqual(s1, s2) } -func (c *ChainConfig) IsEIP158(num *big.Int) bool { - if c.EIP158Block == nil || num == nil { +// isForked returns whether a fork scheduled at block s is active at the given head block. +func isForked(s, head *big.Int) bool { + if s == nil || head == nil { return false } - return num.Cmp(c.EIP158Block) >= 0 + return s.Cmp(head) <= 0 +} + +func configNumEqual(x, y *big.Int) bool { + if x == nil { + return y == nil + } + if y == nil { + return x == nil + } + return x.Cmp(y) == 0 +} +// ConfigCompatError is raised if the locally-stored blockchain is initialised with a +// ChainConfig that would alter the past. +type ConfigCompatError struct { + What string + // block numbers of the stored and new configurations + StoredConfig, NewConfig *big.Int + // the block number to which the local chain must be rewound to correct the error + RewindTo uint64 } -// Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions -// that do not have or require information about the block. -// -// Rules is a one time interface meaning that it shouldn't be used in between transition -// phases. -type Rules struct { - ChainId *big.Int - IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool +func newCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatError { + var rew *big.Int + switch { + case storedblock == nil: + rew = newblock + case newblock == nil || storedblock.Cmp(newblock) < 0: + rew = storedblock + default: + rew = newblock + } + err := &ConfigCompatError{what, storedblock, newblock, 0} + if rew != nil && rew.Sign() > 0 { + err.RewindTo = rew.Uint64() - 1 + } + return err } -func (c *ChainConfig) Rules(num *big.Int) Rules { - return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num)} +func (err *ConfigCompatError) Error() string { + return fmt.Sprintf("mismatching %s in database (have %d, want %d, rewindto %d)", err.What, err.StoredConfig, err.NewConfig, err.RewindTo) } diff --git a/params/config_test.go b/params/config_test.go new file mode 100644 index 000000000..487dc380c --- /dev/null +++ b/params/config_test.go @@ -0,0 +1,81 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package params + +import ( + "math/big" + "reflect" + "testing" +) + +func TestCheckCompatible(t *testing.T) { + type test struct { + stored, new *ChainConfig + head uint64 + wantErr *ConfigCompatError + } + tests := []test{ + {stored: AllProtocolChanges, new: AllProtocolChanges, head: 0, wantErr: nil}, + {stored: AllProtocolChanges, new: AllProtocolChanges, head: 100, wantErr: nil}, + { + stored: &ChainConfig{EIP150Block: big.NewInt(10)}, + new: &ChainConfig{EIP150Block: big.NewInt(20)}, + head: 9, + wantErr: nil, + }, + { + stored: AllProtocolChanges, + new: &ChainConfig{HomesteadBlock: nil}, + head: 3, + wantErr: &ConfigCompatError{ + What: "Homestead fork block", + StoredConfig: big.NewInt(0), + NewConfig: nil, + RewindTo: 0, + }, + }, + { + stored: AllProtocolChanges, + new: &ChainConfig{HomesteadBlock: big.NewInt(1)}, + head: 3, + wantErr: &ConfigCompatError{ + What: "Homestead fork block", + StoredConfig: big.NewInt(0), + NewConfig: big.NewInt(1), + RewindTo: 0, + }, + }, + { + stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)}, + new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)}, + head: 25, + wantErr: &ConfigCompatError{ + What: "EIP150 fork block", + StoredConfig: big.NewInt(10), + NewConfig: big.NewInt(20), + RewindTo: 9, + }, + }, + } + + for _, test := range tests { + err := test.stored.CheckCompatible(test.new, test.head) + if !reflect.DeepEqual(err, test.wantErr) { + t.Errorf("error mismatch:\nstored: %v\nnew: %v\nhead: %v\nerr: %v\nwant: %v", test.stored, test.new, test.head, err, test.wantErr) + } + } +} diff --git a/params/dao.go b/params/dao.go index 3e2a68cc9..ef8e838ff 100644 --- a/params/dao.go +++ b/params/dao.go @@ -17,8 +17,6 @@ package params import ( - "encoding/json" - "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -47,372 +45,123 @@ var DAORefundContract = common.HexToAddress("0xbf4ed7b27f1d666546e30d74d50d173d2 // DAODrainList is the list of accounts whose full balances will be moved into a // refund contract at the beginning of the dao-fork block. -var DAODrainList []common.Address - -func init() { - // Parse the list of DAO accounts to drain - var list []map[string]string - if err := json.Unmarshal([]byte(daoDrainListJSON), &list); err != nil { - panic(fmt.Errorf("Failed to parse DAO drain list: %v", err)) - } - // Collect all the accounts that need draining - for _, dao := range list { - DAODrainList = append(DAODrainList, common.HexToAddress(dao["address"])) - DAODrainList = append(DAODrainList, common.HexToAddress(dao["extraBalanceAccount"])) +func DAODrainList() []common.Address { + return []common.Address{ + common.HexToAddress("0xd4fe7bc31cedb7bfb8a345f31e668033056b2728"), + common.HexToAddress("0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425"), + common.HexToAddress("0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f"), + common.HexToAddress("0xecd135fa4f61a655311e86238c92adcd779555d2"), + common.HexToAddress("0x1975bd06d486162d5dc297798dfc41edd5d160a7"), + common.HexToAddress("0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6"), + common.HexToAddress("0x319f70bab6845585f412ec7724b744fec6095c85"), + common.HexToAddress("0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936"), + common.HexToAddress("0x5c8536898fbb74fc7445814902fd08422eac56d0"), + common.HexToAddress("0x6966ab0d485353095148a2155858910e0965b6f9"), + common.HexToAddress("0x779543a0491a837ca36ce8c635d6154e3c4911a6"), + common.HexToAddress("0x2a5ed960395e2a49b1c758cef4aa15213cfd874c"), + common.HexToAddress("0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5"), + common.HexToAddress("0x9c50426be05db97f5d64fc54bf89eff947f0a321"), + common.HexToAddress("0x200450f06520bdd6c527622a273333384d870efb"), + common.HexToAddress("0xbe8539bfe837b67d1282b2b1d61c3f723966f049"), + common.HexToAddress("0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb"), + common.HexToAddress("0xf1385fb24aad0cd7432824085e42aff90886fef5"), + common.HexToAddress("0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091"), + common.HexToAddress("0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd"), + common.HexToAddress("0x51e0ddd9998364a2eb38588679f0d2c42653e4a6"), + common.HexToAddress("0x627a0a960c079c21c34f7612d5d230e01b4ad4c7"), + common.HexToAddress("0xf0b1aa0eb660754448a7937c022e30aa692fe0c5"), + common.HexToAddress("0x24c4d950dfd4dd1902bbed3508144a54542bba94"), + common.HexToAddress("0x9f27daea7aca0aa0446220b98d028715e3bc803d"), + common.HexToAddress("0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90"), + common.HexToAddress("0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b"), + common.HexToAddress("0x63ed5a272de2f6d968408b4acb9024f4cc208ebf"), + common.HexToAddress("0x6f6704e5a10332af6672e50b3d9754dc460dfa4d"), + common.HexToAddress("0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6"), + common.HexToAddress("0x492ea3bb0f3315521c31f273e565b868fc090f17"), + common.HexToAddress("0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00"), + common.HexToAddress("0x9ea779f907f0b315b364b0cfc39a0fde5b02a416"), + common.HexToAddress("0xceaeb481747ca6c540a000c1f3641f8cef161fa7"), + common.HexToAddress("0xcc34673c6c40e791051898567a1222daf90be287"), + common.HexToAddress("0x579a80d909f346fbfb1189493f521d7f48d52238"), + common.HexToAddress("0xe308bd1ac5fda103967359b2712dd89deffb7973"), + common.HexToAddress("0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c"), + common.HexToAddress("0xac1ecab32727358dba8962a0f3b261731aad9723"), + common.HexToAddress("0x4fd6ace747f06ece9c49699c7cabc62d02211f75"), + common.HexToAddress("0x440c59b325d2997a134c2c7c60a8c61611212bad"), + common.HexToAddress("0x4486a3d68fac6967006d7a517b889fd3f98c102b"), + common.HexToAddress("0x9c15b54878ba618f494b38f0ae7443db6af648ba"), + common.HexToAddress("0x27b137a85656544b1ccb5a0f2e561a5703c6a68f"), + common.HexToAddress("0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241"), + common.HexToAddress("0x23b75c2f6791eef49c69684db4c6c1f93bf49a50"), + common.HexToAddress("0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b"), + common.HexToAddress("0xb9637156d330c0d605a791f1c31ba5890582fe1c"), + common.HexToAddress("0x6131c42fa982e56929107413a9d526fd99405560"), + common.HexToAddress("0x1591fc0f688c81fbeb17f5426a162a7024d430c2"), + common.HexToAddress("0x542a9515200d14b68e934e9830d91645a980dd7a"), + common.HexToAddress("0xc4bbd073882dd2add2424cf47d35213405b01324"), + common.HexToAddress("0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4"), + common.HexToAddress("0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb"), + common.HexToAddress("0x3ba4d81db016dc2890c81f3acec2454bff5aada5"), + common.HexToAddress("0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab"), + common.HexToAddress("0xe4ae1efdfc53b73893af49113d8694a057b9c0d1"), + common.HexToAddress("0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5"), + common.HexToAddress("0x0737a6b837f97f46ebade41b9bc3e1c509c85c53"), + common.HexToAddress("0x97f43a37f595ab5dd318fb46e7a155eae057317a"), + common.HexToAddress("0x52c5317c848ba20c7504cb2c8052abd1fde29d03"), + common.HexToAddress("0x4863226780fe7c0356454236d3b1c8792785748d"), + common.HexToAddress("0x5d2b2e6fcbe3b11d26b525e085ff818dae332479"), + common.HexToAddress("0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c"), + common.HexToAddress("0x057b56736d32b86616a10f619859c6cd6f59092a"), + common.HexToAddress("0x9aa008f65de0b923a2a4f02012ad034a5e2e2192"), + common.HexToAddress("0x304a554a310c7e546dfe434669c62820b7d83490"), + common.HexToAddress("0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79"), + common.HexToAddress("0x4deb0033bb26bc534b197e61d19e0733e5679784"), + common.HexToAddress("0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a"), + common.HexToAddress("0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b"), + common.HexToAddress("0x4fa802324e929786dbda3b8820dc7834e9134a2a"), + common.HexToAddress("0x9da397b9e80755301a3b32173283a91c0ef6c87e"), + common.HexToAddress("0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6"), + common.HexToAddress("0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9"), + common.HexToAddress("0x5dc28b15dffed94048d73806ce4b7a4612a1d48f"), + common.HexToAddress("0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76"), + common.HexToAddress("0x12e626b0eebfe86a56d633b9864e389b45dcb260"), + common.HexToAddress("0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7"), + common.HexToAddress("0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5"), + common.HexToAddress("0xd164b088bd9108b60d0ca3751da4bceb207b0782"), + common.HexToAddress("0x6231b6d0d5e77fe001c2a460bd9584fee60d409b"), + common.HexToAddress("0x1cba23d343a983e9b5cfd19496b9a9701ada385f"), + common.HexToAddress("0xa82f360a8d3455c5c41366975bde739c37bfeb8a"), + common.HexToAddress("0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339"), + common.HexToAddress("0x005f5cee7a43331d5a3d3eec71305925a62f34b6"), + common.HexToAddress("0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d"), + common.HexToAddress("0xd131637d5275fd1a68a3200f4ad25c71a2a9522e"), + common.HexToAddress("0xbc07118b9ac290e4622f5e77a0853539789effbe"), + common.HexToAddress("0x47e7aa56d6bdf3f36be34619660de61275420af8"), + common.HexToAddress("0xacd87e28b0c9d1254e868b81cba4cc20d9a32225"), + common.HexToAddress("0xadf80daec7ba8dcf15392f1ac611fff65d94f880"), + common.HexToAddress("0x5524c55fb03cf21f549444ccbecb664d0acad706"), + common.HexToAddress("0x40b803a9abce16f50f36a77ba41180eb90023925"), + common.HexToAddress("0xfe24cdd8648121a43a7c86d289be4dd2951ed49f"), + common.HexToAddress("0x17802f43a0137c506ba92291391a8a8f207f487d"), + common.HexToAddress("0x253488078a4edf4d6f42f113d1e62836a942cf1a"), + common.HexToAddress("0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915"), + common.HexToAddress("0xb136707642a4ea12fb4bae820f03d2562ebff487"), + common.HexToAddress("0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940"), + common.HexToAddress("0xf14c14075d6c4ed84b86798af0956deef67365b5"), + common.HexToAddress("0xca544e5c4687d109611d0f8f928b53a25af72448"), + common.HexToAddress("0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c"), + common.HexToAddress("0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7"), + common.HexToAddress("0x6d87578288b6cb5549d5076a207456a1f6a63dc0"), + common.HexToAddress("0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e"), + common.HexToAddress("0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6"), + common.HexToAddress("0x2b3455ec7fedf16e646268bf88846bd7a2319bb2"), + common.HexToAddress("0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a"), + common.HexToAddress("0xd343b217de44030afaa275f54d31a9317c7f441e"), + common.HexToAddress("0x84ef4b2357079cd7a7c69fd7a37cd0609a679106"), + common.HexToAddress("0xda2fef9e4a3230988ff17df2165440f37e8b1708"), + common.HexToAddress("0xf4c64518ea10f995918a454158c6b61407ea345c"), + common.HexToAddress("0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97"), + common.HexToAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413"), + common.HexToAddress("0x807640a13483f8ac783c557fcdf27be11ea4ac7a"), } } - -// daoDrainListJSON is the JSON encoded list of accounts whose full balances will -// be moved into a refund contract at the beginning of the dao-fork block. -const daoDrainListJSON = ` -[ - { - "address":"0xd4fe7bc31cedb7bfb8a345f31e668033056b2728", - "balance":"186cc8bfaefb7be", - "extraBalance":"0", - "extraBalanceAccount":"0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425" - }, - { - "address":"0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f", - "balance":"b14e8feab1ff435", - "extraBalance":"0", - "extraBalanceAccount":"0xecd135fa4f61a655311e86238c92adcd779555d2" - }, - { - "address":"0x1975bd06d486162d5dc297798dfc41edd5d160a7", - "balance":"359d26614cb5070c77", - "extraBalance":"0", - "extraBalanceAccount":"0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6" - }, - { - "address":"0x319f70bab6845585f412ec7724b744fec6095c85", - "balance":"6e075cd846d2cb1d42", - "extraBalance":"13d34fd41b545b81", - "extraBalanceAccount":"0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936" - }, - { - "address":"0x5c8536898fbb74fc7445814902fd08422eac56d0", - "balance":"b1e5593558008fd78", - "extraBalance":"0", - "extraBalanceAccount":"0x6966ab0d485353095148a2155858910e0965b6f9" - }, - { - "address":"0x779543a0491a837ca36ce8c635d6154e3c4911a6", - "balance":"392eaa20d1aad59a4c", - "extraBalance":"426938826a96c9", - "extraBalanceAccount":"0x2a5ed960395e2a49b1c758cef4aa15213cfd874c" - }, - { - "address":"0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", - "balance":"2875d22b29793d4ba7", - "extraBalance":"0", - "extraBalanceAccount":"0x9c50426be05db97f5d64fc54bf89eff947f0a321" - }, - { - "address":"0x200450f06520bdd6c527622a273333384d870efb", - "balance":"43c341d9f96954c049", - "extraBalance":"0", - "extraBalanceAccount":"0xbe8539bfe837b67d1282b2b1d61c3f723966f049" - }, - { - "address":"0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", - "balance":"75251057154d70fa816", - "extraBalance":"0", - "extraBalanceAccount":"0xf1385fb24aad0cd7432824085e42aff90886fef5" - }, - { - "address":"0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091", - "balance":"392409769296cf67f36", - "extraBalance":"0", - "extraBalanceAccount":"0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd" - }, - { - "address":"0x51e0ddd9998364a2eb38588679f0d2c42653e4a6", - "balance":"8ac72eccbf4e8083", - "extraBalance":"0", - "extraBalanceAccount":"0x627a0a960c079c21c34f7612d5d230e01b4ad4c7" - }, - { - "address":"0xf0b1aa0eb660754448a7937c022e30aa692fe0c5", - "balance":"82289c3bb3e8c98799", - "extraBalance":"0", - "extraBalanceAccount":"0x24c4d950dfd4dd1902bbed3508144a54542bba94" - }, - { - "address":"0x9f27daea7aca0aa0446220b98d028715e3bc803d", - "balance":"56bc29049ebed40fd", - "extraBalance":"0", - "extraBalanceAccount":"0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90" - }, - { - "address":"0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b", - "balance":"56bc7d3ff79110524", - "extraBalance":"0", - "extraBalanceAccount":"0x63ed5a272de2f6d968408b4acb9024f4cc208ebf" - }, - { - "address":"0x6f6704e5a10332af6672e50b3d9754dc460dfa4d", - "balance":"23b651bd48cbc70cc", - "extraBalance":"0", - "extraBalanceAccount":"0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6" - }, - { - "address":"0x492ea3bb0f3315521c31f273e565b868fc090f17", - "balance":"13ea6d4fee651dd7c9", - "extraBalance":"0", - "extraBalanceAccount":"0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00" - }, - { - "address":"0x9ea779f907f0b315b364b0cfc39a0fde5b02a416", - "balance":"35ac471a3836ae7de5a", - "extraBalance":"0", - "extraBalanceAccount":"0xceaeb481747ca6c540a000c1f3641f8cef161fa7" - }, - { - "address":"0xcc34673c6c40e791051898567a1222daf90be287", - "balance":"d529c0b76b7aa0", - "extraBalance":"0", - "extraBalanceAccount":"0x579a80d909f346fbfb1189493f521d7f48d52238" - }, - { - "address":"0xe308bd1ac5fda103967359b2712dd89deffb7973", - "balance":"5cd9e7df3a8e5cdd3", - "extraBalance":"0", - "extraBalanceAccount":"0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c" - }, - { - "address":"0xac1ecab32727358dba8962a0f3b261731aad9723", - "balance":"2c8442fe35363313b93", - "extraBalance":"0", - "extraBalanceAccount":"0x4fd6ace747f06ece9c49699c7cabc62d02211f75" - }, - { - "address":"0x440c59b325d2997a134c2c7c60a8c61611212bad", - "balance":"e77583a3958130e53", - "extraBalance":"0", - "extraBalanceAccount":"0x4486a3d68fac6967006d7a517b889fd3f98c102b" - }, - { - "address":"0x9c15b54878ba618f494b38f0ae7443db6af648ba", - "balance":"1f0b6ade348ca998", - "extraBalance":"0", - "extraBalanceAccount":"0x27b137a85656544b1ccb5a0f2e561a5703c6a68f" - }, - { - "address":"0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", - "balance":"61725880736659", - "extraBalance":"0", - "extraBalanceAccount":"0x23b75c2f6791eef49c69684db4c6c1f93bf49a50" - }, - { - "address":"0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", - "balance":"42948d8dc7ddbc22d", - "extraBalance":"0", - "extraBalanceAccount":"0xb9637156d330c0d605a791f1c31ba5890582fe1c" - }, - { - "address":"0x6131c42fa982e56929107413a9d526fd99405560", - "balance":"7306683851d1eafbfa", - "extraBalance":"0", - "extraBalanceAccount":"0x1591fc0f688c81fbeb17f5426a162a7024d430c2" - }, - { - "address":"0x542a9515200d14b68e934e9830d91645a980dd7a", - "balance":"2a8457d0d8432e21d0c", - "extraBalance":"0", - "extraBalanceAccount":"0xc4bbd073882dd2add2424cf47d35213405b01324" - }, - { - "address":"0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4", - "balance":"d8d7391feaeaa8cdb", - "extraBalance":"0", - "extraBalanceAccount":"0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb" - }, - { - "address":"0x3ba4d81db016dc2890c81f3acec2454bff5aada5", - "balance":"1", - "extraBalance":"0", - "extraBalanceAccount":"0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab" - }, - { - "address":"0xe4ae1efdfc53b73893af49113d8694a057b9c0d1", - "balance":"456397665fa74041", - "extraBalance":"0", - "extraBalanceAccount":"0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5" - }, - { - "address":"0x0737a6b837f97f46ebade41b9bc3e1c509c85c53", - "balance":"6324dcb7126ecbef", - "extraBalance":"0", - "extraBalanceAccount":"0x97f43a37f595ab5dd318fb46e7a155eae057317a" - }, - { - "address":"0x52c5317c848ba20c7504cb2c8052abd1fde29d03", - "balance":"6c3419b0705c01cd0d", - "extraBalance":"0", - "extraBalanceAccount":"0x4863226780fe7c0356454236d3b1c8792785748d" - }, - { - "address":"0x5d2b2e6fcbe3b11d26b525e085ff818dae332479", - "balance":"456397665fa74041", - "extraBalance":"0", - "extraBalanceAccount":"0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c" - }, - { - "address":"0x057b56736d32b86616a10f619859c6cd6f59092a", - "balance":"232c025bb44b46", - "extraBalance":"0", - "extraBalanceAccount":"0x9aa008f65de0b923a2a4f02012ad034a5e2e2192" - }, - { - "address":"0x304a554a310c7e546dfe434669c62820b7d83490", - "balance":"3034f5ca7d45e17df199b", - "extraBalance":"f7d15162c44e97b6e", - "extraBalanceAccount":"0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79" - }, - { - "address":"0x4deb0033bb26bc534b197e61d19e0733e5679784", - "balance":"4417e96ed796591e09", - "extraBalance":"0", - "extraBalanceAccount":"0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a" - }, - { - "address":"0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b", - "balance":"d3ff7771412bbcc9", - "extraBalance":"0", - "extraBalanceAccount":"0x4fa802324e929786dbda3b8820dc7834e9134a2a" - }, - { - "address":"0x9da397b9e80755301a3b32173283a91c0ef6c87e", - "balance":"32ae324c233816b4c2", - "extraBalance":"0", - "extraBalanceAccount":"0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6" - }, - { - "address":"0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", - "balance":"1e530695b705f037c6", - "extraBalance":"0", - "extraBalanceAccount":"0x5dc28b15dffed94048d73806ce4b7a4612a1d48f" - }, - { - "address":"0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76", - "balance":"68013bad5b4b1133fc5", - "extraBalance":"0", - "extraBalanceAccount":"0x12e626b0eebfe86a56d633b9864e389b45dcb260" - }, - { - "address":"0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7", - "balance":"456397665fa74041", - "extraBalance":"0", - "extraBalanceAccount":"0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5" - }, - { - "address":"0xd164b088bd9108b60d0ca3751da4bceb207b0782", - "balance":"3635ce47fabaaa336e", - "extraBalance":"0", - "extraBalanceAccount":"0x6231b6d0d5e77fe001c2a460bd9584fee60d409b" - }, - { - "address":"0x1cba23d343a983e9b5cfd19496b9a9701ada385f", - "balance":"f3abd9906c170a", - "extraBalance":"0", - "extraBalanceAccount":"0xa82f360a8d3455c5c41366975bde739c37bfeb8a" - }, - { - "address":"0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", - "balance":"4c6679d9d9b95a4e08", - "extraBalance":"0", - "extraBalanceAccount":"0x005f5cee7a43331d5a3d3eec71305925a62f34b6" - }, - { - "address":"0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", - "balance":"40f622936475de31849", - "extraBalance":"671e1bbabded39754", - "extraBalanceAccount":"0xd131637d5275fd1a68a3200f4ad25c71a2a9522e" - }, - { - "address":"0xbc07118b9ac290e4622f5e77a0853539789effbe", - "balance":"1316ccfa4a35db5e58f", - "extraBalance":"0", - "extraBalanceAccount":"0x47e7aa56d6bdf3f36be34619660de61275420af8" - }, - { - "address":"0xacd87e28b0c9d1254e868b81cba4cc20d9a32225", - "balance":"b3ad6bb72000bab9f", - "extraBalance":"0", - "extraBalanceAccount":"0xadf80daec7ba8dcf15392f1ac611fff65d94f880" - }, - { - "address":"0x5524c55fb03cf21f549444ccbecb664d0acad706", - "balance":"16f2da372a5c8a70967", - "extraBalance":"0", - "extraBalanceAccount":"0x40b803a9abce16f50f36a77ba41180eb90023925" - }, - { - "address":"0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", - "balance":"ea0b1bdc78f500a43", - "extraBalance":"0", - "extraBalanceAccount":"0x17802f43a0137c506ba92291391a8a8f207f487d" - }, - { - "address":"0x253488078a4edf4d6f42f113d1e62836a942cf1a", - "balance":"3060e3aed135cc80", - "extraBalance":"0", - "extraBalanceAccount":"0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915" - }, - { - "address":"0xb136707642a4ea12fb4bae820f03d2562ebff487", - "balance":"6050bdeb3354b5c98adc3", - "extraBalance":"0", - "extraBalanceAccount":"0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940" - }, - { - "address":"0xf14c14075d6c4ed84b86798af0956deef67365b5", - "balance":"1d77844e94c25ba2", - "extraBalance":"0", - "extraBalanceAccount":"0xca544e5c4687d109611d0f8f928b53a25af72448" - }, - { - "address":"0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", - "balance":"2e93a72de4fc5ec0ed", - "extraBalance":"0", - "extraBalanceAccount":"0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7" - }, - { - "address":"0x6d87578288b6cb5549d5076a207456a1f6a63dc0", - "balance":"1afd340799e48c18", - "extraBalance":"0", - "extraBalanceAccount":"0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e" - }, - { - "address":"0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", - "balance":"14d0944eb3be947a8", - "extraBalance":"0", - "extraBalanceAccount":"0x2b3455ec7fedf16e646268bf88846bd7a2319bb2" - }, - { - "address":"0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", - "balance":"6202b236a200e365eba", - "extraBalance":"11979be9020f03ec4ec", - "extraBalanceAccount":"0xd343b217de44030afaa275f54d31a9317c7f441e" - }, - { - "address":"0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", - "balance":"7ed634ebbba531901e07", - "extraBalance":"f9c5eff28cb08720c85", - "extraBalanceAccount":"0xda2fef9e4a3230988ff17df2165440f37e8b1708" - }, - { - "address":"0xf4c64518ea10f995918a454158c6b61407ea345c", - "balance":"39152e15508a96ff894a", - "extraBalance":"14041ca908bcc185c8", - "extraBalanceAccount":"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97" - }, - { - "address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413", - "balance":"1", - "extraBalance":"5553ebc", - "extraBalanceAccount":"0x807640a13483f8ac783c557fcdf27be11ea4ac7a" - } -] -` diff --git a/params/util.go b/params/util.go index 546ebb35c..bf833d510 100644 --- a/params/util.go +++ b/params/util.go @@ -38,6 +38,6 @@ var ( TestNetSpuriousDragon = big.NewInt(10) MainNetSpuriousDragon = big.NewInt(2675000) - TestNetChainID = big.NewInt(3) // Test net default chain ID - MainNetChainID = big.NewInt(1) // main net default chain ID + TestNetChainID = big.NewInt(3) // Testnet default chain ID + MainNetChainID = big.NewInt(1) // Mainnet default chain ID ) diff --git a/params/version.go b/params/version.go index fef360473..6a0eb3506 100644 --- a/params/version.go +++ b/params/version.go @@ -16,7 +16,9 @@ package params -import "fmt" +import ( + "fmt" +) const ( VersionMajor = 1 // Major version component of the current release @@ -33,3 +35,11 @@ var Version = func() string { } return v }() + +func VersionWithCommit(gitCommit string) string { + vsn := Version + if len(gitCommit) >= 8 { + vsn += "-" + gitCommit[:8] + } + return vsn +} diff --git a/pow/pow.go b/pow/pow.go deleted file mode 100644 index 4849adb3e..000000000 --- a/pow/pow.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2014 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 <http://www.gnu.org/licenses/>. - -package pow - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -type Block interface { - Difficulty() *big.Int - HashNoNonce() common.Hash - Nonce() uint64 - MixDigest() common.Hash - NumberU64() uint64 -} - -type ChainManager interface { - GetBlockByNumber(uint64) *types.Block - CurrentBlock() *types.Block -} - -type PoW interface { - Verify(block Block) error - Search(block Block, stop <-chan struct{}) (uint64, []byte) - Hashrate() float64 -} - -// FakePow is a non-validating proof of work implementation. -// It returns true from Verify for any block. -type FakePow struct{} - -// Verify implements PoW, returning a success for an input. -func (pow FakePow) Verify(block Block) error { return nil } - -// Search implements PoW, returning the nonce 0 for any call. -func (pow FakePow) Search(block Block, stop <-chan struct{}) (uint64, []byte) { - return 0, nil -} - -// Hashrate implements PoW, returning 0. -func (pow FakePow) Hashrate() float64 { return 0 } diff --git a/rlp/decode.go b/rlp/decode.go index c4e5869cc..ee0b7dbcd 100644 --- a/rlp/decode.go +++ b/rlp/decode.go @@ -63,12 +63,16 @@ type Decoder interface { // must contain an element for each decoded field. Decode returns an // error if there are too few or too many elements. // -// The decoding of struct fields honours two struct tags, "tail" and -// "nil". For an explanation of "tail", see the example. -// The "nil" tag applies to pointer-typed fields and changes the -// decoding rules for the field such that input values of size zero -// decode as a nil pointer. This tag can be useful when decoding -// recursive types. +// The decoding of struct fields honours certain struct tags, "tail", +// "nil" and "-". +// +// The "-" tag ignores fields. +// +// For an explanation of "tail", see the example. +// +// The "nil" tag applies to pointer-typed fields and changes the decoding +// rules for the field such that input values of size zero decode as a nil +// pointer. This tag can be useful when decoding recursive types. // // type StructWithEmptyOK struct { // Foo *[20]byte `rlp:"nil"` diff --git a/rlp/decode_test.go b/rlp/decode_test.go index 2d465b74d..d762e195d 100644 --- a/rlp/decode_test.go +++ b/rlp/decode_test.go @@ -339,6 +339,12 @@ var ( ) ) +type hasIgnoredField struct { + A uint + B uint `rlp:"-"` + C uint +} + var decodeTests = []decodeTest{ // booleans {input: "01", ptr: new(bool), value: true}, @@ -490,6 +496,13 @@ var decodeTests = []decodeTest{ value: tailRaw{A: 1, Tail: []RawValue{}}, }, + // struct tag "-" + { + input: "C20102", + ptr: new(hasIgnoredField), + value: hasIgnoredField{A: 1, C: 2}, + }, + // RawValue {input: "01", ptr: new(RawValue), value: RawValue(unhex("01"))}, {input: "82FFFF", ptr: new(RawValue), value: RawValue(unhex("82FFFF"))}, diff --git a/rlp/encode_test.go b/rlp/encode_test.go index 6f38294e4..827960f7c 100644 --- a/rlp/encode_test.go +++ b/rlp/encode_test.go @@ -218,6 +218,7 @@ var encTests = []encTest{ {val: &tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, output: "C20102"}, {val: &tailRaw{A: 1, Tail: []RawValue{}}, output: "C101"}, {val: &tailRaw{A: 1, Tail: nil}, output: "C101"}, + {val: &hasIgnoredField{A: 1, B: 2, C: 3}, output: "C20103"}, // nil {val: (*uint)(nil), output: "80"}, diff --git a/rlp/typecache.go b/rlp/typecache.go index a2f217c66..3df799e1e 100644 --- a/rlp/typecache.go +++ b/rlp/typecache.go @@ -37,11 +37,12 @@ type typeinfo struct { type tags struct { // rlp:"nil" controls whether empty input results in a nil pointer. nilOK bool - // rlp:"tail" controls whether this field swallows additional list // elements. It can only be set for the last field, which must be // of slice type. tail bool + // rlp:"-" ignores fields. + ignored bool } type typekey struct { @@ -101,6 +102,9 @@ func structFields(typ reflect.Type) (fields []field, err error) { if err != nil { return nil, err } + if tags.ignored { + continue + } info, err := cachedTypeInfo1(f.Type, tags) if err != nil { return nil, err @@ -117,6 +121,8 @@ func parseStructTag(typ reflect.Type, fi int) (tags, error) { for _, t := range strings.Split(f.Tag.Get("rlp"), ",") { switch t = strings.TrimSpace(t); t { case "": + case "-": + ts.ignored = true case "nil": ts.nilOK = true case "tail": diff --git a/rpc/client.go b/rpc/client.go index 78a6fe789..2c35ba54a 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -19,6 +19,7 @@ package rpc import ( "bytes" "container/list" + "context" "encoding/json" "errors" "fmt" @@ -31,7 +32,6 @@ import ( "time" "github.com/ethereum/go-ethereum/log" - "golang.org/x/net/context" ) var ( diff --git a/rpc/client_context_go1.4.go b/rpc/client_context_go1.4.go deleted file mode 100644 index ac956a17d..000000000 --- a/rpc/client_context_go1.4.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// +build !go1.5 - -package rpc - -import ( - "net" - "net/http" - "time" - - "golang.org/x/net/context" -) - -// In older versions of Go (below 1.5), dials cannot be canceled -// via a channel or context. The context deadline can still applied. - -// contextDialer returns a dialer that applies the deadline value from the given context. -func contextDialer(ctx context.Context) *net.Dialer { - dialer := &net.Dialer{KeepAlive: tcpKeepAliveInterval} - if deadline, ok := ctx.Deadline(); ok { - dialer.Deadline = deadline - } else { - dialer.Deadline = time.Now().Add(defaultDialTimeout) - } - return dialer -} - -// dialContext connects to the given address, aborting the dial if ctx is canceled. -func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { - return contextDialer(ctx).Dial(network, addr) -} - -// requestWithContext copies req, adding the cancelation channel and deadline from ctx. -func requestWithContext(c *http.Client, req *http.Request, ctx context.Context) (*http.Client, *http.Request) { - // Set Timeout on the client if the context has a deadline. - // Note that there is no default timeout (unlike in contextDialer) because - // the timeout applies to the entire request, including reads from body. - if deadline, ok := ctx.Deadline(); ok { - c2 := *c - c2.Timeout = deadline.Sub(time.Now()) - c = &c2 - } - req2 := *req - return c, &req2 -} diff --git a/rpc/client_context_go1.5.go b/rpc/client_context_go1.5.go deleted file mode 100644 index 4a007d9f8..000000000 --- a/rpc/client_context_go1.5.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// +build go1.5,!go1.6 - -package rpc - -import ( - "net" - "net/http" - "time" - - "golang.org/x/net/context" -) - -// In Go 1.5, dials cannot be canceled via a channel or context. The context deadline can -// still be applied. Go 1.5 adds the ability to cancel HTTP requests via a channel. - -// contextDialer returns a dialer that applies the deadline value from the given context. -func contextDialer(ctx context.Context) *net.Dialer { - dialer := &net.Dialer{KeepAlive: tcpKeepAliveInterval} - if deadline, ok := ctx.Deadline(); ok { - dialer.Deadline = deadline - } else { - dialer.Deadline = time.Now().Add(defaultDialTimeout) - } - return dialer -} - -// dialContext connects to the given address, aborting the dial if ctx is canceled. -func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { - return contextDialer(ctx).Dial(network, addr) -} - -// requestWithContext copies req, adding the cancelation channel and deadline from ctx. -func requestWithContext(c *http.Client, req *http.Request, ctx context.Context) (*http.Client, *http.Request) { - // Set Timeout on the client if the context has a deadline. - // Note that there is no default timeout (unlike in contextDialer) because - // the timeout applies to the entire request, including reads from body. - if deadline, ok := ctx.Deadline(); ok { - c2 := *c - c2.Timeout = deadline.Sub(time.Now()) - c = &c2 - } - req2 := *req - req2.Cancel = ctx.Done() - return c, &req2 -} diff --git a/rpc/client_context_go1.6.go b/rpc/client_context_go1.6.go deleted file mode 100644 index 67777ddc6..000000000 --- a/rpc/client_context_go1.6.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// +build go1.6,!go1.7 - -package rpc - -import ( - "net" - "net/http" - "time" - - "golang.org/x/net/context" -) - -// In Go 1.6, net.Dialer gained the ability to cancel via a channel. - -// contextDialer returns a dialer that applies the deadline value from the given context. -func contextDialer(ctx context.Context) *net.Dialer { - dialer := &net.Dialer{Cancel: ctx.Done(), KeepAlive: tcpKeepAliveInterval} - if deadline, ok := ctx.Deadline(); ok { - dialer.Deadline = deadline - } else { - dialer.Deadline = time.Now().Add(defaultDialTimeout) - } - return dialer -} - -// dialContext connects to the given address, aborting the dial if ctx is canceled. -func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { - return contextDialer(ctx).Dial(network, addr) -} - -// requestWithContext copies req, adding the cancelation channel and deadline from ctx. -func requestWithContext(c *http.Client, req *http.Request, ctx context.Context) (*http.Client, *http.Request) { - // We set Timeout on the client for Go <= 1.5. There - // is no need to do that here because the dial will be canceled - // by package http. - req2 := *req - req2.Cancel = ctx.Done() - return c, &req2 -} diff --git a/rpc/client_context_go1.7.go b/rpc/client_context_go1.7.go deleted file mode 100644 index 56ce12ab8..000000000 --- a/rpc/client_context_go1.7.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. - -// +build go1.7 - -package rpc - -import ( - "context" - "net" - "net/http" - "time" -) - -// In Go 1.7, context moved into the standard library and support -// for cancelation via context was added to net.Dialer and http.Request. - -// contextDialer returns a dialer that applies the deadline value from the given context. -func contextDialer(ctx context.Context) *net.Dialer { - dialer := &net.Dialer{Cancel: ctx.Done(), KeepAlive: tcpKeepAliveInterval} - if deadline, ok := ctx.Deadline(); ok { - dialer.Deadline = deadline - } else { - dialer.Deadline = time.Now().Add(defaultDialTimeout) - } - return dialer -} - -// dialContext connects to the given address, aborting the dial if ctx is canceled. -func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { - d := &net.Dialer{KeepAlive: tcpKeepAliveInterval} - return d.DialContext(ctx, network, addr) -} - -// requestWithContext copies req, adding the cancelation channel and deadline from ctx. -func requestWithContext(c *http.Client, req *http.Request, ctx context.Context) (*http.Client, *http.Request) { - return c, req.WithContext(ctx) -} diff --git a/rpc/client_example_test.go b/rpc/client_example_test.go index 3462b3685..8276a9ead 100644 --- a/rpc/client_example_test.go +++ b/rpc/client_example_test.go @@ -17,12 +17,12 @@ package rpc_test import ( + "context" "fmt" "math/big" "time" "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" ) // In this example, our client whishes to track the latest 'block number' diff --git a/rpc/client_test.go b/rpc/client_test.go index 407ed9c06..10d74670b 100644 --- a/rpc/client_test.go +++ b/rpc/client_test.go @@ -17,6 +17,7 @@ package rpc import ( + "context" "fmt" "math/rand" "net" @@ -31,7 +32,6 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/log" - "golang.org/x/net/context" ) func TestClientRequest(t *testing.T) { @@ -394,7 +394,7 @@ func TestClientReconnect(t *testing.T) { if err != nil { t.Fatal(err) } - go http.Serve(l, srv.WebsocketHandler("*")) + go http.Serve(l, srv.WebsocketHandler([]string{"*"})) return srv, l } @@ -466,7 +466,7 @@ func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, var hs *httptest.Server switch transport { case "ws": - hs = httptest.NewUnstartedServer(srv.WebsocketHandler("*")) + hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"})) case "http": hs = httptest.NewUnstartedServer(srv) default: diff --git a/rpc/http.go b/rpc/http.go index 7d4fe5d47..022f9ce8f 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -18,18 +18,17 @@ package rpc import ( "bytes" + "context" "encoding/json" "fmt" "io" "io/ioutil" "net" "net/http" - "strings" "sync" "time" "github.com/rs/cors" - "golang.org/x/net/context" ) const ( @@ -115,11 +114,11 @@ func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadClos if err != nil { return nil, err } - client, req := requestWithContext(hc.client, hc.req, ctx) + req := hc.req.WithContext(ctx) req.Body = ioutil.NopCloser(bytes.NewReader(body)) req.ContentLength = int64(len(body)) - resp, err := client.Do(req) + resp, err := hc.client.Do(req) if err != nil { return nil, err } @@ -140,8 +139,8 @@ func (t *httpReadWriteNopCloser) Close() error { // NewHTTPServer creates a new HTTP RPC server around an API provider. // // Deprecated: Server implements http.Handler -func NewHTTPServer(corsString string, srv *Server) *http.Server { - return &http.Server{Handler: newCorsHandler(srv, corsString)} +func NewHTTPServer(cors []string, srv *Server) *http.Server { + return &http.Server{Handler: newCorsHandler(srv, cors)} } // ServeHTTP serves JSON-RPC requests over HTTP. @@ -162,15 +161,12 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { srv.ServeSingleRequest(codec, OptionMethodInvocation) } -func newCorsHandler(srv *Server, corsString string) http.Handler { - var allowedOrigins []string - for _, domain := range strings.Split(corsString, ",") { - allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain)) - } +func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler { c := cors.New(cors.Options{ AllowedOrigins: allowedOrigins, AllowedMethods: []string{"POST", "GET"}, MaxAge: 600, + AllowedHeaders: []string{"*"}, }) return c.Handler(srv) } diff --git a/rpc/inproc.go b/rpc/inproc.go index f72b97497..595a7ca65 100644 --- a/rpc/inproc.go +++ b/rpc/inproc.go @@ -17,9 +17,8 @@ package rpc import ( + "context" "net" - - "golang.org/x/net/context" ) // NewInProcClient attaches an in-process connection to the given RPC server. diff --git a/rpc/ipc.go b/rpc/ipc.go index 3c86d711c..8de18a56f 100644 --- a/rpc/ipc.go +++ b/rpc/ipc.go @@ -17,12 +17,11 @@ package rpc import ( + "context" "fmt" "net" "github.com/ethereum/go-ethereum/log" - - "golang.org/x/net/context" ) // CreateIPCListener creates an listener, on Unix platforms this is a unix socket, on diff --git a/rpc/ipc_unix.go b/rpc/ipc_unix.go index a25b21627..0851ea61e 100644 --- a/rpc/ipc_unix.go +++ b/rpc/ipc_unix.go @@ -19,11 +19,10 @@ package rpc import ( + "context" "net" "os" "path/filepath" - - "golang.org/x/net/context" ) // ipcListen will create a Unix socket on the given endpoint. diff --git a/rpc/ipc_windows.go b/rpc/ipc_windows.go index 68234d215..ca56a3ce4 100644 --- a/rpc/ipc_windows.go +++ b/rpc/ipc_windows.go @@ -19,10 +19,10 @@ package rpc import ( + "context" "net" "time" - "golang.org/x/net/context" "gopkg.in/natefinch/npipe.v2" ) diff --git a/rpc/server.go b/rpc/server.go index 4f9ce541e..78df37e52 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -17,23 +17,21 @@ package rpc import ( + "context" "fmt" "reflect" "runtime" + "sync" "sync/atomic" "github.com/ethereum/go-ethereum/log" - - "golang.org/x/net/context" "gopkg.in/fatih/set.v0" ) const ( notificationBufferSize = 10000 // max buffered notifications before codec is closed - MetadataApi = "rpc" - DefaultIPCApis = "admin,debug,eth,miner,net,personal,shh,txpool,web3" - DefaultHTTPApis = "eth,net,web3" + MetadataApi = "rpc" ) // CodecOption specifies which type of messages this codec supports @@ -144,6 +142,8 @@ func hasOption(option CodecOption, options []CodecOption) bool { // requests until the codec returns an error when reading a request (in most cases // an EOF). It executes requests in parallel when singleShot is false. func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecOption) error { + var pend sync.WaitGroup + defer func() { if err := recover(); err != nil { const size = 64 << 10 @@ -151,7 +151,6 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO buf = buf[:runtime.Stack(buf, false)] log.Error(fmt.Sprint(string(buf))) } - s.codecsMu.Lock() s.codecs.Remove(codec) s.codecsMu.Unlock() @@ -180,8 +179,13 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO for atomic.LoadInt32(&s.run) == 1 { reqs, batch, err := s.readRequest(codec) if err != nil { - log.Debug(fmt.Sprintf("read error %v\n", err)) - codec.Write(codec.CreateErrorResponse(nil, err)) + // If a parsing error occurred, send an error + if err.Error() != "EOF" { + log.Debug(fmt.Sprintf("read error %v\n", err)) + codec.Write(codec.CreateErrorResponse(nil, err)) + } + // Error or end of stream, wait for requests and tear down + pend.Wait() return nil } @@ -200,20 +204,27 @@ func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecO } return nil } - - if singleShot && batch { - s.execBatch(ctx, codec, reqs) - return nil - } else if singleShot && !batch { - s.exec(ctx, codec, reqs[0]) + // If a single shot request is executing, run and return immediately + if singleShot { + if batch { + s.execBatch(ctx, codec, reqs) + } else { + s.exec(ctx, codec, reqs[0]) + } return nil - } else if !singleShot && batch { - go s.execBatch(ctx, codec, reqs) - } else { - go s.exec(ctx, codec, reqs[0]) } - } + // For multi-shot connections, start a goroutine to serve and loop back + pend.Add(1) + go func(reqs []*serverRequest, batch bool) { + defer pend.Done() + if batch { + s.execBatch(ctx, codec, reqs) + } else { + s.exec(ctx, codec, reqs[0]) + } + }(reqs, batch) + } return nil } diff --git a/rpc/server_test.go b/rpc/server_test.go index c3c88fab7..90d62f26d 100644 --- a/rpc/server_test.go +++ b/rpc/server_test.go @@ -17,13 +17,12 @@ package rpc import ( + "context" "encoding/json" "net" "reflect" "testing" "time" - - "golang.org/x/net/context" ) type Service struct{} diff --git a/rpc/subscription.go b/rpc/subscription.go index bcdc3cdfc..9ab6af9e1 100644 --- a/rpc/subscription.go +++ b/rpc/subscription.go @@ -17,10 +17,9 @@ package rpc import ( + "context" "errors" "sync" - - "golang.org/x/net/context" ) var ( diff --git a/rpc/subscription_test.go b/rpc/subscription_test.go index 00c4e0e35..345b4e5f2 100644 --- a/rpc/subscription_test.go +++ b/rpc/subscription_test.go @@ -17,13 +17,12 @@ package rpc import ( + "context" "encoding/json" "net" "sync" "testing" "time" - - "golang.org/x/net/context" ) type NotificationTestService struct { diff --git a/rpc/utils.go b/rpc/utils.go index c249e9b4a..2506c4833 100644 --- a/rpc/utils.go +++ b/rpc/utils.go @@ -18,6 +18,7 @@ package rpc import ( "bufio" + "context" crand "crypto/rand" "encoding/binary" "encoding/hex" @@ -29,8 +30,6 @@ import ( "time" "unicode" "unicode/utf8" - - "golang.org/x/net/context" ) var ( diff --git a/rpc/websocket.go b/rpc/websocket.go index f4271fda8..5f9593a43 100644 --- a/rpc/websocket.go +++ b/rpc/websocket.go @@ -17,6 +17,7 @@ package rpc import ( + "context" "crypto/tls" "fmt" "net" @@ -24,10 +25,9 @@ import ( "net/url" "os" "strings" + "time" "github.com/ethereum/go-ethereum/log" - - "golang.org/x/net/context" "golang.org/x/net/websocket" "gopkg.in/fatih/set.v0" ) @@ -36,9 +36,9 @@ import ( // // allowedOrigins should be a comma-separated list of allowed origin URLs. // To allow connections with any origin, pass "*". -func (srv *Server) WebsocketHandler(allowedOrigins string) http.Handler { +func (srv *Server) WebsocketHandler(allowedOrigins []string) http.Handler { return websocket.Server{ - Handshake: wsHandshakeValidator(strings.Split(allowedOrigins, ",")), + Handshake: wsHandshakeValidator(allowedOrigins), Handler: func(conn *websocket.Conn) { srv.ServeCodec(NewJSONCodec(conn), OptionMethodInvocation|OptionSubscriptions) }, @@ -48,7 +48,7 @@ func (srv *Server) WebsocketHandler(allowedOrigins string) http.Handler { // NewWSServer creates a new websocket RPC server around an API provider. // // Deprecated: use Server.WebsocketHandler -func NewWSServer(allowedOrigins string, srv *Server) *http.Server { +func NewWSServer(allowedOrigins []string, srv *Server) *http.Server { return &http.Server{Handler: srv.WebsocketHandler(allowedOrigins)} } @@ -150,3 +150,18 @@ func wsDialAddress(location *url.URL) string { } return location.Host } + +func dialContext(ctx context.Context, network, addr string) (net.Conn, error) { + d := &net.Dialer{KeepAlive: tcpKeepAliveInterval} + return d.DialContext(ctx, network, addr) +} + +func contextDialer(ctx context.Context) *net.Dialer { + dialer := &net.Dialer{Cancel: ctx.Done(), KeepAlive: tcpKeepAliveInterval} + if deadline, ok := ctx.Deadline(); ok { + dialer.Deadline = deadline + } else { + dialer.Deadline = time.Now().Add(defaultDialTimeout) + } + return dialer +} diff --git a/swarm/api/api.go b/swarm/api/api.go index 7af27208d..26a9445d5 100644 --- a/swarm/api/api.go +++ b/swarm/api/api.go @@ -24,9 +24,13 @@ import ( "strings" "sync" + "bytes" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/swarm/storage" + "mime" + "path/filepath" + "time" ) var ( @@ -58,6 +62,13 @@ func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) { return } +// to be used only in TEST +func (self *Api) Upload(uploadDir, index string) (hash string, err error) { + fs := NewFileSystem(self) + hash, err = fs.Upload(uploadDir, index) + return hash, err +} + // DPA reader API func (self *Api) Retrieve(key storage.Key) storage.LazySectionReader { return self.dpa.Retrieve(key) @@ -70,86 +81,53 @@ func (self *Api) Store(data io.Reader, size int64, wg *sync.WaitGroup) (key stor type ErrResolve error // DNS Resolver -func (self *Api) Resolve(hostPort string, nameresolver bool) (storage.Key, error) { - log.Trace(fmt.Sprintf("Resolving : %v", hostPort)) - if hashMatcher.MatchString(hostPort) || self.dns == nil { - log.Trace(fmt.Sprintf("host is a contentHash: '%v'", hostPort)) - return storage.Key(common.Hex2Bytes(hostPort)), nil +func (self *Api) Resolve(uri *URI) (storage.Key, error) { + log.Trace(fmt.Sprintf("Resolving : %v", uri.Addr)) + + var err error + if !uri.Immutable() { + if self.dns != nil { + resolved, err := self.dns.Resolve(uri.Addr) + if err == nil { + return resolved[:], nil + } + } else { + err = fmt.Errorf("no DNS to resolve name") + } } - if !nameresolver { - return nil, fmt.Errorf("'%s' is not a content hash value.", hostPort) + if hashMatcher.MatchString(uri.Addr) { + return storage.Key(common.Hex2Bytes(uri.Addr)), nil } - contentHash, err := self.dns.Resolve(hostPort) if err != nil { - err = ErrResolve(err) - log.Warn(fmt.Sprintf("DNS error : %v", err)) - } - log.Trace(fmt.Sprintf("host lookup: %v -> %v", hostPort, contentHash)) - return contentHash[:], err -} -func Parse(uri string) (hostPort, path string) { - if uri == "" { - return + return nil, fmt.Errorf("'%s' does not resolve: %v but is not a content hash", uri.Addr, err) } - parts := slashes.Split(uri, 3) - var i int - if len(parts) == 0 { - return - } - // beginning with slash is now optional - for len(parts[i]) == 0 { - i++ - } - hostPort = parts[i] - for i < len(parts)-1 { - i++ - if len(path) > 0 { - path = path + "/" + parts[i] - } else { - path = parts[i] - } - } - log.Debug(fmt.Sprintf("host: '%s', path '%s' requested.", hostPort, path)) - return + return nil, fmt.Errorf("'%s' is not a content hash", uri.Addr) } -func (self *Api) parseAndResolve(uri string, nameresolver bool) (key storage.Key, hostPort, path string, err error) { - hostPort, path = Parse(uri) - //resolving host and port - contentHash, err := self.Resolve(hostPort, nameresolver) - log.Debug(fmt.Sprintf("Resolved '%s' to contentHash: '%s', path: '%s'", uri, contentHash, path)) - return contentHash[:], hostPort, path, err -} // Put provides singleton manifest creation on top of dpa store -func (self *Api) Put(content, contentType string) (string, error) { +func (self *Api) Put(content, contentType string) (storage.Key, error) { r := strings.NewReader(content) wg := &sync.WaitGroup{} key, err := self.dpa.Store(r, int64(len(content)), wg, nil) if err != nil { - return "", err + return nil, err } manifest := fmt.Sprintf(`{"entries":[{"hash":"%v","contentType":"%s"}]}`, key, contentType) r = strings.NewReader(manifest) key, err = self.dpa.Store(r, int64(len(manifest)), wg, nil) if err != nil { - return "", err + return nil, err } wg.Wait() - return key.String(), nil + return key, nil } // Get uses iterative manifest retrieval and prefix matching -// to resolve path to content using dpa retrieve +// to resolve basePath to content using dpa retrieve // it returns a section reader, mimeType, status and an error -func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionReader, mimeType string, status int, err error) { - key, _, path, err := self.parseAndResolve(uri, nameresolver) - if err != nil { - return nil, "", 500, fmt.Errorf("can't resolve: %v", err) - } - - quitC := make(chan bool) - trie, err := loadManifest(self.dpa, key, quitC) +func (self *Api) Get(key storage.Key, path string) (reader storage.LazySectionReader, mimeType string, status int, err error) { + trie, err := loadManifest(self.dpa, key, nil) if err != nil { log.Warn(fmt.Sprintf("loadManifestTrie error: %v", err)) return @@ -173,32 +151,203 @@ func (self *Api) Get(uri string, nameresolver bool) (reader storage.LazySectionR return } -func (self *Api) Modify(uri, contentHash, contentType string, nameresolver bool) (newRootHash string, err error) { - root, _, path, err := self.parseAndResolve(uri, nameresolver) - if err != nil { - return "", fmt.Errorf("can't resolve: %v", err) - } - +func (self *Api) Modify(key storage.Key, path, contentHash, contentType string) (storage.Key, error) { quitC := make(chan bool) - trie, err := loadManifest(self.dpa, root, quitC) + trie, err := loadManifest(self.dpa, key, quitC) if err != nil { - return + return nil, err } - if contentHash != "" { - entry := &manifestTrieEntry{ + entry := newManifestTrieEntry(&ManifestEntry{ Path: path, - Hash: contentHash, ContentType: contentType, - } + }, nil) + entry.Hash = contentHash trie.addEntry(entry, quitC) } else { trie.deleteEntry(path, quitC) } - err = trie.recalcAndStore() + if err := trie.recalcAndStore(); err != nil { + return nil, err + } + return trie.hash, nil +} + +func (self *Api) AddFile(mhash, path, fname string, content []byte, nameresolver bool) (storage.Key, string, error) { + + uri, err := Parse("bzz:/" + mhash) if err != nil { - return + return nil, "", err + } + mkey, err := self.Resolve(uri) + if err != nil { + return nil, "", err + } + + // trim the root dir we added + if path[:1] == "/" { + path = path[1:] + } + + entry := &ManifestEntry{ + Path: filepath.Join(path, fname), + ContentType: mime.TypeByExtension(filepath.Ext(fname)), + Mode: 0700, + Size: int64(len(content)), + ModTime: time.Now(), + } + + mw, err := self.NewManifestWriter(mkey, nil) + if err != nil { + return nil, "", err + } + + fkey, err := mw.AddEntry(bytes.NewReader(content), entry) + if err != nil { + return nil, "", err + } + + newMkey, err := mw.Store() + if err != nil { + return nil, "", err + + } + + return fkey, newMkey.String(), nil + +} + +func (self *Api) RemoveFile(mhash, path, fname string, nameresolver bool) (string, error) { + + uri, err := Parse("bzz:/" + mhash) + if err != nil { + return "", err + } + mkey, err := self.Resolve(uri) + if err != nil { + return "", err + } + + // trim the root dir we added + if path[:1] == "/" { + path = path[1:] + } + + mw, err := self.NewManifestWriter(mkey, nil) + if err != nil { + return "", err + } + + err = mw.RemoveEntry(filepath.Join(path, fname)) + if err != nil { + return "", err + } + + newMkey, err := mw.Store() + if err != nil { + return "", err + + } + + return newMkey.String(), nil +} + +func (self *Api) AppendFile(mhash, path, fname string, existingSize int64, content []byte, oldKey storage.Key, offset int64, addSize int64, nameresolver bool) (storage.Key, string, error) { + + buffSize := offset + addSize + if buffSize < existingSize { + buffSize = existingSize + } + + buf := make([]byte, buffSize) + + oldReader := self.Retrieve(oldKey) + io.ReadAtLeast(oldReader, buf, int(offset)) + + newReader := bytes.NewReader(content) + io.ReadAtLeast(newReader, buf[offset:], int(addSize)) + + if buffSize < existingSize { + io.ReadAtLeast(oldReader, buf[addSize:], int(buffSize)) } - return trie.hash.String(), nil + + combinedReader := bytes.NewReader(buf) + totalSize := int64(len(buf)) + + // TODO(jmozah): to append using pyramid chunker when it is ready + //oldReader := self.Retrieve(oldKey) + //newReader := bytes.NewReader(content) + //combinedReader := io.MultiReader(oldReader, newReader) + + uri, err := Parse("bzz:/" + mhash) + if err != nil { + return nil, "", err + } + mkey, err := self.Resolve(uri) + if err != nil { + return nil, "", err + } + + // trim the root dir we added + if path[:1] == "/" { + path = path[1:] + } + + mw, err := self.NewManifestWriter(mkey, nil) + if err != nil { + return nil, "", err + } + + err = mw.RemoveEntry(filepath.Join(path, fname)) + if err != nil { + return nil, "", err + } + + entry := &ManifestEntry{ + Path: filepath.Join(path, fname), + ContentType: mime.TypeByExtension(filepath.Ext(fname)), + Mode: 0700, + Size: totalSize, + ModTime: time.Now(), + } + + fkey, err := mw.AddEntry(io.Reader(combinedReader), entry) + if err != nil { + return nil, "", err + } + + newMkey, err := mw.Store() + if err != nil { + return nil, "", err + + } + + return fkey, newMkey.String(), nil + +} + +func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storage.Key, manifestEntryMap map[string]*manifestTrieEntry, err error) { + + uri, err := Parse("bzz:/" + mhash) + if err != nil { + return nil, nil, err + } + key, err = self.Resolve(uri) + if err != nil { + return nil, nil, err + } + + quitC := make(chan bool) + rootTrie, err := loadManifest(self.dpa, key, quitC) + if err != nil { + return nil, nil, fmt.Errorf("can't load manifest %v: %v", key.String(), err) + } + + manifestEntryMap = map[string]*manifestTrieEntry{} + err = rootTrie.listWithPrefix(uri.Path, quitC, func(entry *manifestTrieEntry, suffix string) { + manifestEntryMap[suffix] = entry + }) + + return key, manifestEntryMap, nil } diff --git a/swarm/api/api_test.go b/swarm/api/api_test.go index 16e90dd32..c2d78c2dc 100644 --- a/swarm/api/api_test.go +++ b/swarm/api/api_test.go @@ -23,6 +23,7 @@ import ( "os" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/swarm/storage" ) @@ -81,8 +82,9 @@ func expResponse(content string, mimeType string, status int) *Response { } // func testGet(t *testing.T, api *Api, bzzhash string) *testResponse { -func testGet(t *testing.T, api *Api, bzzhash string) *testResponse { - reader, mimeType, status, err := api.Get(bzzhash, true) +func testGet(t *testing.T, api *Api, bzzhash, path string) *testResponse { + key := storage.Key(common.Hex2Bytes(bzzhash)) + reader, mimeType, status, err := api.Get(key, path) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -107,11 +109,11 @@ func TestApiPut(t *testing.T) { content := "hello" exp := expResponse(content, "text/plain", 0) // exp := expResponse([]byte(content), "text/plain", 0) - bzzhash, err := api.Put(content, exp.MimeType) + key, err := api.Put(content, exp.MimeType) if err != nil { t.Fatalf("unexpected error: %v", err) } - resp := testGet(t, api, bzzhash) + resp := testGet(t, api, key.String(), "") checkResponse(t, resp, exp) }) } diff --git a/swarm/api/client/client.go b/swarm/api/client/client.go new file mode 100644 index 000000000..f9c3e51e8 --- /dev/null +++ b/swarm/api/client/client.go @@ -0,0 +1,465 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package client + +import ( + "archive/tar" + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "mime" + "mime/multipart" + "net/http" + "net/textproto" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/swarm/api" +) + +var ( + DefaultGateway = "http://localhost:8500" + DefaultClient = NewClient(DefaultGateway) +) + +func NewClient(gateway string) *Client { + return &Client{ + Gateway: gateway, + } +} + +// Client wraps interaction with a swarm HTTP gateway. +type Client struct { + Gateway string +} + +// UploadRaw uploads raw data to swarm and returns the resulting hash +func (c *Client) UploadRaw(r io.Reader, size int64) (string, error) { + if size <= 0 { + return "", errors.New("data size must be greater than zero") + } + req, err := http.NewRequest("POST", c.Gateway+"/bzzr:/", r) + if err != nil { + return "", err + } + req.ContentLength = size + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("unexpected HTTP status: %s", res.Status) + } + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", err + } + return string(data), nil +} + +// DownloadRaw downloads raw data from swarm +func (c *Client) DownloadRaw(hash string) (io.ReadCloser, error) { + uri := c.Gateway + "/bzzr:/" + hash + res, err := http.DefaultClient.Get(uri) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusOK { + res.Body.Close() + return nil, fmt.Errorf("unexpected HTTP status: %s", res.Status) + } + return res.Body, nil +} + +// File represents a file in a swarm manifest and is used for uploading and +// downloading content to and from swarm +type File struct { + io.ReadCloser + api.ManifestEntry +} + +// Open opens a local file which can then be passed to client.Upload to upload +// it to swarm +func Open(path string) (*File, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + stat, err := f.Stat() + if err != nil { + f.Close() + return nil, err + } + return &File{ + ReadCloser: f, + ManifestEntry: api.ManifestEntry{ + ContentType: mime.TypeByExtension(filepath.Ext(path)), + Mode: int64(stat.Mode()), + Size: stat.Size(), + ModTime: stat.ModTime(), + }, + }, nil +} + +// Upload uploads a file to swarm and either adds it to an existing manifest +// (if the manifest argument is non-empty) or creates a new manifest containing +// the file, returning the resulting manifest hash (the file will then be +// available at bzz:/<hash>/<path>) +func (c *Client) Upload(file *File, manifest string) (string, error) { + if file.Size <= 0 { + return "", errors.New("file size must be greater than zero") + } + return c.TarUpload(manifest, &FileUploader{file}) +} + +// Download downloads a file with the given path from the swarm manifest with +// the given hash (i.e. it gets bzz:/<hash>/<path>) +func (c *Client) Download(hash, path string) (*File, error) { + uri := c.Gateway + "/bzz:/" + hash + "/" + path + res, err := http.DefaultClient.Get(uri) + if err != nil { + return nil, err + } + if res.StatusCode != http.StatusOK { + res.Body.Close() + return nil, fmt.Errorf("unexpected HTTP status: %s", res.Status) + } + return &File{ + ReadCloser: res.Body, + ManifestEntry: api.ManifestEntry{ + ContentType: res.Header.Get("Content-Type"), + Size: res.ContentLength, + }, + }, nil +} + +// UploadDirectory uploads a directory tree to swarm and either adds the files +// to an existing manifest (if the manifest argument is non-empty) or creates a +// new manifest, returning the resulting manifest hash (files from the +// directory will then be available at bzz:/<hash>/path/to/file), with +// the file specified in defaultPath being uploaded to the root of the manifest +// (i.e. bzz:/<hash>/) +func (c *Client) UploadDirectory(dir, defaultPath, manifest string) (string, error) { + stat, err := os.Stat(dir) + if err != nil { + return "", err + } else if !stat.IsDir() { + return "", fmt.Errorf("not a directory: %s", dir) + } + return c.TarUpload(manifest, &DirectoryUploader{dir, defaultPath}) +} + +// DownloadDirectory downloads the files contained in a swarm manifest under +// the given path into a local directory (existing files will be overwritten) +func (c *Client) DownloadDirectory(hash, path, destDir string) error { + stat, err := os.Stat(destDir) + if err != nil { + return err + } else if !stat.IsDir() { + return fmt.Errorf("not a directory: %s", destDir) + } + + uri := c.Gateway + "/bzz:/" + hash + "/" + path + req, err := http.NewRequest("GET", uri, nil) + if err != nil { + return err + } + req.Header.Set("Accept", "application/x-tar") + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected HTTP status: %s", res.Status) + } + tr := tar.NewReader(res.Body) + for { + hdr, err := tr.Next() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + // ignore the default path file + if hdr.Name == "" { + continue + } + + dstPath := filepath.Join(destDir, filepath.Clean(strings.TrimPrefix(hdr.Name, path))) + if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil { + return err + } + var mode os.FileMode = 0644 + if hdr.Mode > 0 { + mode = os.FileMode(hdr.Mode) + } + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) + if err != nil { + return err + } + n, err := io.Copy(dst, tr) + dst.Close() + if err != nil { + return err + } else if n != hdr.Size { + return fmt.Errorf("expected %s to be %d bytes but got %d", hdr.Name, hdr.Size, n) + } + } +} + +// UploadManifest uploads the given manifest to swarm +func (c *Client) UploadManifest(m *api.Manifest) (string, error) { + data, err := json.Marshal(m) + if err != nil { + return "", err + } + return c.UploadRaw(bytes.NewReader(data), int64(len(data))) +} + +// DownloadManifest downloads a swarm manifest +func (c *Client) DownloadManifest(hash string) (*api.Manifest, error) { + res, err := c.DownloadRaw(hash) + if err != nil { + return nil, err + } + defer res.Close() + var manifest api.Manifest + if err := json.NewDecoder(res).Decode(&manifest); err != nil { + return nil, err + } + return &manifest, nil +} + +// List list files in a swarm manifest which have the given prefix, grouping +// common prefixes using "/" as a delimiter. +// +// For example, if the manifest represents the following directory structure: +// +// file1.txt +// file2.txt +// dir1/file3.txt +// dir1/dir2/file4.txt +// +// Then: +// +// - a prefix of "" would return [dir1/, file1.txt, file2.txt] +// - a prefix of "file" would return [file1.txt, file2.txt] +// - a prefix of "dir1/" would return [dir1/dir2/, dir1/file3.txt] +// +// where entries ending with "/" are common prefixes. +func (c *Client) List(hash, prefix string) (*api.ManifestList, error) { + res, err := http.DefaultClient.Get(c.Gateway + "/bzz:/" + hash + "/" + prefix + "?list=true") + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected HTTP status: %s", res.Status) + } + var list api.ManifestList + if err := json.NewDecoder(res.Body).Decode(&list); err != nil { + return nil, err + } + return &list, nil +} + +// Uploader uploads files to swarm using a provided UploadFn +type Uploader interface { + Upload(UploadFn) error +} + +type UploaderFunc func(UploadFn) error + +func (u UploaderFunc) Upload(upload UploadFn) error { + return u(upload) +} + +// DirectoryUploader uploads all files in a directory, optionally uploading +// a file to the default path +type DirectoryUploader struct { + Dir string + DefaultPath string +} + +// Upload performs the upload of the directory and default path +func (d *DirectoryUploader) Upload(upload UploadFn) error { + if d.DefaultPath != "" { + file, err := Open(d.DefaultPath) + if err != nil { + return err + } + if err := upload(file); err != nil { + return err + } + } + return filepath.Walk(d.Dir, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + if f.IsDir() { + return nil + } + file, err := Open(path) + if err != nil { + return err + } + relPath, err := filepath.Rel(d.Dir, path) + if err != nil { + return err + } + file.Path = filepath.ToSlash(relPath) + return upload(file) + }) +} + +// FileUploader uploads a single file +type FileUploader struct { + File *File +} + +// Upload performs the upload of the file +func (f *FileUploader) Upload(upload UploadFn) error { + return upload(f.File) +} + +// UploadFn is the type of function passed to an Uploader to perform the upload +// of a single file (for example, a directory uploader would call a provided +// UploadFn for each file in the directory tree) +type UploadFn func(file *File) error + +// TarUpload uses the given Uploader to upload files to swarm as a tar stream, +// returning the resulting manifest hash +func (c *Client) TarUpload(hash string, uploader Uploader) (string, error) { + reqR, reqW := io.Pipe() + defer reqR.Close() + req, err := http.NewRequest("POST", c.Gateway+"/bzz:/"+hash, reqR) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/x-tar") + + // use 'Expect: 100-continue' so we don't send the request body if + // the server refuses the request + req.Header.Set("Expect", "100-continue") + + tw := tar.NewWriter(reqW) + + // define an UploadFn which adds files to the tar stream + uploadFn := func(file *File) error { + hdr := &tar.Header{ + Name: file.Path, + Mode: file.Mode, + Size: file.Size, + ModTime: file.ModTime, + Xattrs: map[string]string{ + "user.swarm.content-type": file.ContentType, + }, + } + if err := tw.WriteHeader(hdr); err != nil { + return err + } + _, err = io.Copy(tw, file) + return err + } + + // run the upload in a goroutine so we can send the request headers and + // wait for a '100 Continue' response before sending the tar stream + go func() { + err := uploader.Upload(uploadFn) + if err == nil { + err = tw.Close() + } + reqW.CloseWithError(err) + }() + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("unexpected HTTP status: %s", res.Status) + } + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", err + } + return string(data), nil +} + +// MultipartUpload uses the given Uploader to upload files to swarm as a +// multipart form, returning the resulting manifest hash +func (c *Client) MultipartUpload(hash string, uploader Uploader) (string, error) { + reqR, reqW := io.Pipe() + defer reqR.Close() + req, err := http.NewRequest("POST", c.Gateway+"/bzz:/"+hash, reqR) + if err != nil { + return "", err + } + + // use 'Expect: 100-continue' so we don't send the request body if + // the server refuses the request + req.Header.Set("Expect", "100-continue") + + mw := multipart.NewWriter(reqW) + req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%q", mw.Boundary())) + + // define an UploadFn which adds files to the multipart form + uploadFn := func(file *File) error { + hdr := make(textproto.MIMEHeader) + hdr.Set("Content-Disposition", fmt.Sprintf("form-data; name=%q", file.Path)) + hdr.Set("Content-Type", file.ContentType) + hdr.Set("Content-Length", strconv.FormatInt(file.Size, 10)) + w, err := mw.CreatePart(hdr) + if err != nil { + return err + } + _, err = io.Copy(w, file) + return err + } + + // run the upload in a goroutine so we can send the request headers and + // wait for a '100 Continue' response before sending the multipart form + go func() { + err := uploader.Upload(uploadFn) + if err == nil { + err = mw.Close() + } + reqW.CloseWithError(err) + }() + + res, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("unexpected HTTP status: %s", res.Status) + } + data, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", err + } + return string(data), nil +} diff --git a/swarm/api/client/client_test.go b/swarm/api/client/client_test.go new file mode 100644 index 000000000..4d02ceaf4 --- /dev/null +++ b/swarm/api/client/client_test.go @@ -0,0 +1,327 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package client + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "sort" + "testing" + + "github.com/ethereum/go-ethereum/swarm/api" + "github.com/ethereum/go-ethereum/swarm/testutil" +) + +// TestClientUploadDownloadRaw test uploading and downloading raw data to swarm +func TestClientUploadDownloadRaw(t *testing.T) { + srv := testutil.NewTestSwarmServer(t) + defer srv.Close() + + client := NewClient(srv.URL) + + // upload some raw data + data := []byte("foo123") + hash, err := client.UploadRaw(bytes.NewReader(data), int64(len(data))) + if err != nil { + t.Fatal(err) + } + + // check we can download the same data + res, err := client.DownloadRaw(hash) + if err != nil { + t.Fatal(err) + } + defer res.Close() + gotData, err := ioutil.ReadAll(res) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(gotData, data) { + t.Fatalf("expected downloaded data to be %q, got %q", data, gotData) + } +} + +// TestClientUploadDownloadFiles test uploading and downloading files to swarm +// manifests +func TestClientUploadDownloadFiles(t *testing.T) { + srv := testutil.NewTestSwarmServer(t) + defer srv.Close() + + client := NewClient(srv.URL) + upload := func(manifest, path string, data []byte) string { + file := &File{ + ReadCloser: ioutil.NopCloser(bytes.NewReader(data)), + ManifestEntry: api.ManifestEntry{ + Path: path, + ContentType: "text/plain", + Size: int64(len(data)), + }, + } + hash, err := client.Upload(file, manifest) + if err != nil { + t.Fatal(err) + } + return hash + } + checkDownload := func(manifest, path string, expected []byte) { + file, err := client.Download(manifest, path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + if file.Size != int64(len(expected)) { + t.Fatalf("expected downloaded file to be %d bytes, got %d", len(expected), file.Size) + } + if file.ContentType != file.ContentType { + t.Fatalf("expected downloaded file to have type %q, got %q", file.ContentType, file.ContentType) + } + data, err := ioutil.ReadAll(file) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(data, expected) { + t.Fatalf("expected downloaded data to be %q, got %q", expected, data) + } + } + + // upload a file to the root of a manifest + rootData := []byte("some-data") + rootHash := upload("", "", rootData) + + // check we can download the root file + checkDownload(rootHash, "", rootData) + + // upload another file to the same manifest + otherData := []byte("some-other-data") + newHash := upload(rootHash, "some/other/path", otherData) + + // check we can download both files from the new manifest + checkDownload(newHash, "", rootData) + checkDownload(newHash, "some/other/path", otherData) + + // replace the root file with different data + newHash = upload(newHash, "", otherData) + + // check both files have the other data + checkDownload(newHash, "", otherData) + checkDownload(newHash, "some/other/path", otherData) +} + +var testDirFiles = []string{ + "file1.txt", + "file2.txt", + "dir1/file3.txt", + "dir1/file4.txt", + "dir2/file5.txt", + "dir2/dir3/file6.txt", + "dir2/dir4/file7.txt", + "dir2/dir4/file8.txt", +} + +func newTestDirectory(t *testing.T) string { + dir, err := ioutil.TempDir("", "swarm-client-test") + if err != nil { + t.Fatal(err) + } + + for _, file := range testDirFiles { + path := filepath.Join(dir, file) + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + os.RemoveAll(dir) + t.Fatalf("error creating dir for %s: %s", path, err) + } + if err := ioutil.WriteFile(path, []byte(file), 0644); err != nil { + os.RemoveAll(dir) + t.Fatalf("error writing file %s: %s", path, err) + } + } + + return dir +} + +// TestClientUploadDownloadDirectory tests uploading and downloading a +// directory of files to a swarm manifest +func TestClientUploadDownloadDirectory(t *testing.T) { + srv := testutil.NewTestSwarmServer(t) + defer srv.Close() + + dir := newTestDirectory(t) + defer os.RemoveAll(dir) + + // upload the directory + client := NewClient(srv.URL) + defaultPath := filepath.Join(dir, testDirFiles[0]) + hash, err := client.UploadDirectory(dir, defaultPath, "") + if err != nil { + t.Fatalf("error uploading directory: %s", err) + } + + // check we can download the individual files + checkDownloadFile := func(path string, expected []byte) { + file, err := client.Download(hash, path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + data, err := ioutil.ReadAll(file) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(data, expected) { + t.Fatalf("expected data to be %q, got %q", expected, data) + } + } + for _, file := range testDirFiles { + checkDownloadFile(file, []byte(file)) + } + + // check we can download the default path + checkDownloadFile("", []byte(testDirFiles[0])) + + // check we can download the directory + tmp, err := ioutil.TempDir("", "swarm-client-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmp) + if err := client.DownloadDirectory(hash, "", tmp); err != nil { + t.Fatal(err) + } + for _, file := range testDirFiles { + data, err := ioutil.ReadFile(filepath.Join(tmp, file)) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(data, []byte(file)) { + t.Fatalf("expected data to be %q, got %q", file, data) + } + } +} + +// TestClientFileList tests listing files in a swarm manifest +func TestClientFileList(t *testing.T) { + srv := testutil.NewTestSwarmServer(t) + defer srv.Close() + + dir := newTestDirectory(t) + defer os.RemoveAll(dir) + + client := NewClient(srv.URL) + hash, err := client.UploadDirectory(dir, "", "") + if err != nil { + t.Fatalf("error uploading directory: %s", err) + } + + ls := func(prefix string) []string { + list, err := client.List(hash, prefix) + if err != nil { + t.Fatal(err) + } + paths := make([]string, 0, len(list.CommonPrefixes)+len(list.Entries)) + for _, prefix := range list.CommonPrefixes { + paths = append(paths, prefix) + } + for _, entry := range list.Entries { + paths = append(paths, entry.Path) + } + sort.Strings(paths) + return paths + } + + tests := map[string][]string{ + "": []string{"dir1/", "dir2/", "file1.txt", "file2.txt"}, + "file": []string{"file1.txt", "file2.txt"}, + "file1": []string{"file1.txt"}, + "file2.txt": []string{"file2.txt"}, + "file12": []string{}, + "dir": []string{"dir1/", "dir2/"}, + "dir1": []string{"dir1/"}, + "dir1/": []string{"dir1/file3.txt", "dir1/file4.txt"}, + "dir1/file": []string{"dir1/file3.txt", "dir1/file4.txt"}, + "dir1/file3.txt": []string{"dir1/file3.txt"}, + "dir1/file34": []string{}, + "dir2/": []string{"dir2/dir3/", "dir2/dir4/", "dir2/file5.txt"}, + "dir2/file": []string{"dir2/file5.txt"}, + "dir2/dir": []string{"dir2/dir3/", "dir2/dir4/"}, + "dir2/dir3/": []string{"dir2/dir3/file6.txt"}, + "dir2/dir4/": []string{"dir2/dir4/file7.txt", "dir2/dir4/file8.txt"}, + "dir2/dir4/file": []string{"dir2/dir4/file7.txt", "dir2/dir4/file8.txt"}, + "dir2/dir4/file7.txt": []string{"dir2/dir4/file7.txt"}, + "dir2/dir4/file78": []string{}, + } + for prefix, expected := range tests { + actual := ls(prefix) + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected prefix %q to return %v, got %v", prefix, expected, actual) + } + } +} + +// TestClientMultipartUpload tests uploading files to swarm using a multipart +// upload +func TestClientMultipartUpload(t *testing.T) { + srv := testutil.NewTestSwarmServer(t) + defer srv.Close() + + // define an uploader which uploads testDirFiles with some data + data := []byte("some-data") + uploader := UploaderFunc(func(upload UploadFn) error { + for _, name := range testDirFiles { + file := &File{ + ReadCloser: ioutil.NopCloser(bytes.NewReader(data)), + ManifestEntry: api.ManifestEntry{ + Path: name, + ContentType: "text/plain", + Size: int64(len(data)), + }, + } + if err := upload(file); err != nil { + return err + } + } + return nil + }) + + // upload the files as a multipart upload + client := NewClient(srv.URL) + hash, err := client.MultipartUpload("", uploader) + if err != nil { + t.Fatal(err) + } + + // check we can download the individual files + checkDownloadFile := func(path string) { + file, err := client.Download(hash, path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + gotData, err := ioutil.ReadAll(file) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(gotData, data) { + t.Fatalf("expected data to be %q, got %q", data, gotData) + } + } + for _, file := range testDirFiles { + checkDownloadFile(file) + } +} diff --git a/swarm/api/filesystem.go b/swarm/api/filesystem.go index c2583e265..f5dc90e2e 100644 --- a/swarm/api/filesystem.go +++ b/swarm/api/filesystem.go @@ -22,6 +22,7 @@ import ( "io" "net/http" "os" + "path" "path/filepath" "sync" @@ -43,6 +44,8 @@ func NewFileSystem(api *Api) *FileSystem { // Upload replicates a local directory as a manifest file and uploads it // using dpa store // TODO: localpath should point to a manifest +// +// DEPRECATED: Use the HTTP API instead func (self *FileSystem) Upload(lpath, index string) (string, error) { var list []*manifestTrieEntry localpath, err := filepath.Abs(filepath.Clean(lpath)) @@ -65,16 +68,13 @@ func (self *FileSystem) Upload(lpath, index string) (string, error) { log.Debug(fmt.Sprintf("uploading '%s'", localpath)) err = filepath.Walk(localpath, func(path string, info os.FileInfo, err error) error { if (err == nil) && !info.IsDir() { - //fmt.Printf("lp %s path %s\n", localpath, path) if len(path) <= start { return fmt.Errorf("Path is too short") } if path[:start] != localpath { return fmt.Errorf("Path prefix of '%s' does not match localpath '%s'", path, localpath) } - entry := &manifestTrieEntry{ - Path: filepath.ToSlash(path), - } + entry := newManifestTrieEntry(&ManifestEntry{Path: filepath.ToSlash(path)}, nil) list = append(list, entry) } return err @@ -91,9 +91,7 @@ func (self *FileSystem) Upload(lpath, index string) (string, error) { if localpath[:start] != dir { return "", fmt.Errorf("Path prefix of '%s' does not match dir '%s'", localpath, dir) } - entry := &manifestTrieEntry{ - Path: filepath.ToSlash(localpath), - } + entry := newManifestTrieEntry(&ManifestEntry{Path: filepath.ToSlash(localpath)}, nil) list = append(list, entry) } @@ -153,11 +151,10 @@ func (self *FileSystem) Upload(lpath, index string) (string, error) { } entry.Path = RegularSlashes(entry.Path[start:]) if entry.Path == index { - ientry := &manifestTrieEntry{ - Path: "", - Hash: entry.Hash, + ientry := newManifestTrieEntry(&ManifestEntry{ ContentType: entry.ContentType, - } + }, nil) + ientry.Hash = entry.Hash trie.addEntry(ientry, quitC) } trie.addEntry(entry, quitC) @@ -172,8 +169,10 @@ func (self *FileSystem) Upload(lpath, index string) (string, error) { return hs, err2 } -// Download replicates the manifest path structure on the local filesystem +// Download replicates the manifest basePath structure on the local filesystem // under localpath +// +// DEPRECATED: Use the HTTP API instead func (self *FileSystem) Download(bzzpath, localpath string) error { lpath, err := filepath.Abs(filepath.Clean(localpath)) if err != nil { @@ -185,10 +184,15 @@ func (self *FileSystem) Download(bzzpath, localpath string) error { } //resolving host and port - key, _, path, err := self.api.parseAndResolve(bzzpath, true) + uri, err := Parse(path.Join("bzz:/", bzzpath)) + if err != nil { + return err + } + key, err := self.api.Resolve(uri) if err != nil { return err } + path := uri.Path if len(path) > 0 { path += "/" @@ -264,7 +268,7 @@ func (self *FileSystem) Download(bzzpath, localpath string) error { } func retrieveToFile(quitC chan bool, dpa *storage.DPA, key storage.Key, path string) error { - f, err := os.Create(path) // TODO: path separators + f, err := os.Create(path) // TODO: basePath separators if err != nil { return err } diff --git a/swarm/api/filesystem_test.go b/swarm/api/filesystem_test.go index 4a27cb1da..8a15e735d 100644 --- a/swarm/api/filesystem_test.go +++ b/swarm/api/filesystem_test.go @@ -23,6 +23,9 @@ import ( "path/filepath" "sync" "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/swarm/storage" ) var testDownloadDir, _ = ioutil.TempDir(os.TempDir(), "bzz-test") @@ -51,16 +54,17 @@ func TestApiDirUpload0(t *testing.T) { t.Fatalf("unexpected error: %v", err) } content := readPath(t, "testdata", "test0", "index.html") - resp := testGet(t, api, bzzhash+"/index.html") + resp := testGet(t, api, bzzhash, "index.html") exp := expResponse(content, "text/html; charset=utf-8", 0) checkResponse(t, resp, exp) content = readPath(t, "testdata", "test0", "index.css") - resp = testGet(t, api, bzzhash+"/index.css") + resp = testGet(t, api, bzzhash, "index.css") exp = expResponse(content, "text/css", 0) checkResponse(t, resp, exp) - _, _, _, err = api.Get(bzzhash, true) + key := storage.Key(common.Hex2Bytes(bzzhash)) + _, _, _, err = api.Get(key, "") if err == nil { t.Fatalf("expected error: %v", err) } @@ -90,7 +94,8 @@ func TestApiDirUploadModify(t *testing.T) { return } - bzzhash, err = api.Modify(bzzhash+"/index.html", "", "", true) + key := storage.Key(common.Hex2Bytes(bzzhash)) + key, err = api.Modify(key, "index.html", "", "") if err != nil { t.Errorf("unexpected error: %v", err) return @@ -107,32 +112,33 @@ func TestApiDirUploadModify(t *testing.T) { t.Errorf("unexpected error: %v", err) return } - bzzhash, err = api.Modify(bzzhash+"/index2.html", hash.Hex(), "text/html; charset=utf-8", true) + key, err = api.Modify(key, "index2.html", hash.Hex(), "text/html; charset=utf-8") if err != nil { t.Errorf("unexpected error: %v", err) return } - bzzhash, err = api.Modify(bzzhash+"/img/logo.png", hash.Hex(), "text/html; charset=utf-8", true) + key, err = api.Modify(key, "img/logo.png", hash.Hex(), "text/html; charset=utf-8") if err != nil { t.Errorf("unexpected error: %v", err) return } + bzzhash = key.String() content := readPath(t, "testdata", "test0", "index.html") - resp := testGet(t, api, bzzhash+"/index2.html") + resp := testGet(t, api, bzzhash, "index2.html") exp := expResponse(content, "text/html; charset=utf-8", 0) checkResponse(t, resp, exp) - resp = testGet(t, api, bzzhash+"/img/logo.png") + resp = testGet(t, api, bzzhash, "img/logo.png") exp = expResponse(content, "text/html; charset=utf-8", 0) checkResponse(t, resp, exp) content = readPath(t, "testdata", "test0", "index.css") - resp = testGet(t, api, bzzhash+"/index.css") + resp = testGet(t, api, bzzhash, "index.css") exp = expResponse(content, "text/css", 0) checkResponse(t, resp, exp) - _, _, _, err = api.Get(bzzhash, true) + _, _, _, err = api.Get(key, "") if err == nil { t.Errorf("expected error: %v", err) } @@ -149,7 +155,7 @@ func TestApiDirUploadWithRootFile(t *testing.T) { } content := readPath(t, "testdata", "test0", "index.html") - resp := testGet(t, api, bzzhash) + resp := testGet(t, api, bzzhash, "") exp := expResponse(content, "text/html; charset=utf-8", 0) checkResponse(t, resp, exp) }) @@ -165,7 +171,7 @@ func TestApiFileUpload(t *testing.T) { } content := readPath(t, "testdata", "test0", "index.html") - resp := testGet(t, api, bzzhash+"/index.html") + resp := testGet(t, api, bzzhash, "index.html") exp := expResponse(content, "text/html; charset=utf-8", 0) checkResponse(t, resp, exp) }) @@ -181,7 +187,7 @@ func TestApiFileUploadWithRootFile(t *testing.T) { } content := readPath(t, "testdata", "test0", "index.html") - resp := testGet(t, api, bzzhash) + resp := testGet(t, api, bzzhash, "") exp := expResponse(content, "text/html; charset=utf-8", 0) checkResponse(t, resp, exp) }) diff --git a/swarm/api/http/roundtripper_test.go b/swarm/api/http/roundtripper_test.go index fc74f5d3a..f99c4f35e 100644 --- a/swarm/api/http/roundtripper_test.go +++ b/swarm/api/http/roundtripper_test.go @@ -18,14 +18,14 @@ package http import ( "io/ioutil" + "net" "net/http" + "net/http/httptest" "strings" "testing" "time" ) -const port = "3222" - func TestRoundTripper(t *testing.T) { serveMux := http.NewServeMux() serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { @@ -36,9 +36,12 @@ func TestRoundTripper(t *testing.T) { http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed) } }) - go http.ListenAndServe(":"+port, serveMux) - rt := &RoundTripper{Port: port} + srv := httptest.NewServer(serveMux) + defer srv.Close() + + host, port, _ := net.SplitHostPort(srv.Listener.Addr().String()) + rt := &RoundTripper{Host: host, Port: port} trans := &http.Transport{} trans.RegisterProtocol("bzz", rt) client := &http.Client{Transport: trans} diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go index a61696678..849b9e10f 100644 --- a/swarm/api/http/server.go +++ b/swarm/api/http/server.go @@ -20,13 +20,19 @@ A simple http server interface to Swarm package http import ( - "bytes" + "archive/tar" + "encoding/json" + "errors" "fmt" "io" + "io/ioutil" + "mime" + "mime/multipart" "net/http" - "regexp" + "os" + "path" + "strconv" "strings" - "sync" "time" "github.com/ethereum/go-ethereum/common" @@ -36,29 +42,9 @@ import ( "github.com/rs/cors" ) -const ( - rawType = "application/octet-stream" -) - -var ( - // accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw) - bzzPrefix = regexp.MustCompile("^/+bzz[ir]?:/+") - trailingSlashes = regexp.MustCompile("/+$") - rootDocumentUri = regexp.MustCompile("^/+bzz[i]?:/+[^/]+$") - // forever = func() time.Time { return time.Unix(0, 0) } - forever = time.Now -) - -type sequentialReader struct { - reader io.Reader - pos int64 - ahead map[int64](chan bool) - lock sync.Mutex -} - -// Server is the basic configuration needs for the HTTP server and also +// ServerConfig is the basic configuration needed for the HTTP server and also // includes CORS settings. -type Server struct { +type ServerConfig struct { Addr string CorsString string } @@ -69,262 +55,594 @@ type Server struct { // https://github.com/atom/electron/blob/master/docs/api/protocol.md // starts up http server -func StartHttpServer(api *api.Api, server *Server) { - serveMux := http.NewServeMux() - serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - handler(w, r, api) - }) +func StartHttpServer(api *api.Api, config *ServerConfig) { var allowedOrigins []string - for _, domain := range strings.Split(server.CorsString, ",") { + for _, domain := range strings.Split(config.CorsString, ",") { allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain)) } c := cors.New(cors.Options{ AllowedOrigins: allowedOrigins, AllowedMethods: []string{"POST", "GET", "DELETE", "PATCH", "PUT"}, MaxAge: 600, + AllowedHeaders: []string{"*"}, }) - hdlr := c.Handler(serveMux) + hdlr := c.Handler(NewServer(api)) - go http.ListenAndServe(server.Addr, hdlr) - log.Info(fmt.Sprintf("Swarm HTTP proxy started on localhost:%s", server.Addr)) + go http.ListenAndServe(config.Addr, hdlr) + log.Info(fmt.Sprintf("Swarm HTTP proxy started on localhost:%s", config.Addr)) } -func handler(w http.ResponseWriter, r *http.Request, a *api.Api) { - requestURL := r.URL - // This is wrong - // if requestURL.Host == "" { - // var err error - // requestURL, err = url.Parse(r.Referer() + requestURL.String()) - // if err != nil { - // http.Error(w, err.Error(), http.StatusBadRequest) - // return - // } - // } - log.Debug(fmt.Sprintf("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept"))) - uri := requestURL.Path - var raw, nameresolver bool - var proto string - - // HTTP-based URL protocol handler - log.Debug(fmt.Sprintf("BZZ request URI: '%s'", uri)) - - path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string { - proto = p - return "" - }) +func NewServer(api *api.Api) *Server { + return &Server{api} +} + +type Server struct { + api *api.Api +} - // protocol identification (ugly) - if proto == "" { - log.Error(fmt.Sprintf("[BZZ] Swarm: Protocol error in request `%s`.", uri)) - http.Error(w, "Invalid request URL: need access protocol (bzz:/, bzzr:/, bzzi:/) as first element in path.", http.StatusBadRequest) +// Request wraps http.Request and also includes the parsed bzz URI +type Request struct { + http.Request + + uri *api.URI +} + +// HandlePostRaw handles a POST request to a raw bzzr:/ URI, stores the request +// body in swarm and returns the resulting storage key as a text/plain response +func (s *Server) HandlePostRaw(w http.ResponseWriter, r *Request) { + if r.uri.Path != "" { + s.BadRequest(w, r, "raw POST request cannot contain a path") + return + } + + if r.Header.Get("Content-Length") == "" { + s.BadRequest(w, r, "missing Content-Length header in request") return } - if len(proto) > 4 { - raw = proto[1:5] == "bzzr" - nameresolver = proto[1:5] != "bzzi" + + key, err := s.api.Store(r.Body, r.ContentLength, nil) + if err != nil { + s.Error(w, r, err) + return } + s.logDebug("content for %s stored", key.Log()) + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, key) +} - log.Debug("", "msg", log.Lazy{Fn: func() string { - return fmt.Sprintf("[BZZ] Swarm: %s request over protocol %s '%s' received.", r.Method, proto, path) - }}) +// HandlePostFiles handles a POST request (or deprecated PUT request) to +// bzz:/<hash>/<path> which contains either a single file or multiple files +// (either a tar archive or multipart form), adds those files either to an +// existing manifest or to a new manifest under <path> and returns the +// resulting manifest hash as a text/plain response +func (s *Server) HandlePostFiles(w http.ResponseWriter, r *Request) { + contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err != nil { + s.BadRequest(w, r, err.Error()) + return + } - switch { - case r.Method == "POST" || r.Method == "PUT": - if r.Header.Get("content-length") == "" { - http.Error(w, "Missing Content-Length header in request.", http.StatusBadRequest) + var key storage.Key + if r.uri.Addr != "" { + key, err = s.api.Resolve(r.uri) + if err != nil { + s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) return } - key, err := a.Store(io.LimitReader(r.Body, r.ContentLength), r.ContentLength, nil) - if err == nil { - log.Debug(fmt.Sprintf("Content for %v stored", key.Log())) - } else { - http.Error(w, err.Error(), http.StatusBadRequest) + } else { + key, err = s.api.NewManifest() + if err != nil { + s.Error(w, r, err) return } - if r.Method == "POST" { - if raw { - w.Header().Set("Content-Type", "text/plain") - http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(common.Bytes2Hex(key)))) - } else { - http.Error(w, "No POST to "+uri+" allowed.", http.StatusBadRequest) - return + } + + newKey, err := s.updateManifest(key, func(mw *api.ManifestWriter) error { + switch contentType { + + case "application/x-tar": + return s.handleTarUpload(r, mw) + + case "multipart/form-data": + return s.handleMultipartUpload(r, params["boundary"], mw) + + default: + return s.handleDirectUpload(r, mw) + } + }) + if err != nil { + s.Error(w, r, fmt.Errorf("error creating manifest: %s", err)) + return + } + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, newKey) +} + +func (s *Server) handleTarUpload(req *Request, mw *api.ManifestWriter) error { + tr := tar.NewReader(req.Body) + for { + hdr, err := tr.Next() + if err == io.EOF { + return nil + } else if err != nil { + return fmt.Errorf("error reading tar stream: %s", err) + } + + // only store regular files + if !hdr.FileInfo().Mode().IsRegular() { + continue + } + + // add the entry under the path from the request + path := path.Join(req.uri.Path, hdr.Name) + entry := &api.ManifestEntry{ + Path: path, + ContentType: hdr.Xattrs["user.swarm.content-type"], + Mode: hdr.Mode, + Size: hdr.Size, + ModTime: hdr.ModTime, + } + s.logDebug("adding %s (%d bytes) to new manifest", entry.Path, entry.Size) + contentKey, err := mw.AddEntry(tr, entry) + if err != nil { + return fmt.Errorf("error adding manifest entry from tar stream: %s", err) + } + s.logDebug("content for %s stored", contentKey.Log()) + } +} + +func (s *Server) handleMultipartUpload(req *Request, boundary string, mw *api.ManifestWriter) error { + mr := multipart.NewReader(req.Body, boundary) + for { + part, err := mr.NextPart() + if err == io.EOF { + return nil + } else if err != nil { + return fmt.Errorf("error reading multipart form: %s", err) + } + + var size int64 + var reader io.Reader = part + if contentLength := part.Header.Get("Content-Length"); contentLength != "" { + size, err = strconv.ParseInt(contentLength, 10, 64) + if err != nil { + return fmt.Errorf("error parsing multipart content length: %s", err) } + reader = part } else { - // PUT - if raw { - http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest) - return - } else { - path = api.RegularSlashes(path) - mime := r.Header.Get("Content-Type") - // TODO proper root hash separation - log.Debug(fmt.Sprintf("Modify '%s' to store %v as '%s'.", path, key.Log(), mime)) - newKey, err := a.Modify(path, common.Bytes2Hex(key), mime, nameresolver) - if err == nil { - log.Debug(fmt.Sprintf("Swarm replaced manifest by '%s'", newKey)) - w.Header().Set("Content-Type", "text/plain") - http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey))) - } else { - http.Error(w, "PUT to "+path+"failed.", http.StatusBadRequest) - return - } + // copy the part to a tmp file to get its size + tmp, err := ioutil.TempFile("", "swarm-multipart") + if err != nil { + return err } - } - case r.Method == "DELETE": - if raw { - http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest) - return - } else { - path = api.RegularSlashes(path) - log.Debug(fmt.Sprintf("Delete '%s'.", path)) - newKey, err := a.Modify(path, "", "", nameresolver) - if err == nil { - log.Debug(fmt.Sprintf("Swarm replaced manifest by '%s'", newKey)) - w.Header().Set("Content-Type", "text/plain") - http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey))) - } else { - http.Error(w, "DELETE to "+path+"failed.", http.StatusBadRequest) - return + defer os.Remove(tmp.Name()) + defer tmp.Close() + size, err = io.Copy(tmp, part) + if err != nil { + return fmt.Errorf("error copying multipart content: %s", err) + } + if _, err := tmp.Seek(0, os.SEEK_SET); err != nil { + return fmt.Errorf("error copying multipart content: %s", err) } + reader = tmp + } + + // add the entry under the path from the request + name := part.FileName() + if name == "" { + name = part.FormName() + } + path := path.Join(req.uri.Path, name) + entry := &api.ManifestEntry{ + Path: path, + ContentType: part.Header.Get("Content-Type"), + Size: size, + ModTime: time.Now(), } - case r.Method == "GET" || r.Method == "HEAD": - path = trailingSlashes.ReplaceAllString(path, "") - if path == "" { - http.Error(w, "Empty path not allowed", http.StatusBadRequest) + s.logDebug("adding %s (%d bytes) to new manifest", entry.Path, entry.Size) + contentKey, err := mw.AddEntry(reader, entry) + if err != nil { + return fmt.Errorf("error adding manifest entry from multipart form: %s", err) + } + s.logDebug("content for %s stored", contentKey.Log()) + } +} + +func (s *Server) handleDirectUpload(req *Request, mw *api.ManifestWriter) error { + key, err := mw.AddEntry(req.Body, &api.ManifestEntry{ + Path: req.uri.Path, + ContentType: req.Header.Get("Content-Type"), + Mode: 0644, + Size: req.ContentLength, + ModTime: time.Now(), + }) + if err != nil { + return err + } + s.logDebug("content for %s stored", key.Log()) + return nil +} + +// HandleDelete handles a DELETE request to bzz:/<manifest>/<path>, removes +// <path> from <manifest> and returns the resulting manifest hash as a +// text/plain response +func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) { + key, err := s.api.Resolve(r.uri) + if err != nil { + s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) + return + } + + newKey, err := s.updateManifest(key, func(mw *api.ManifestWriter) error { + s.logDebug("removing %s from manifest %s", r.uri.Path, key.Log()) + return mw.RemoveEntry(r.uri.Path) + }) + if err != nil { + s.Error(w, r, fmt.Errorf("error updating manifest: %s", err)) + return + } + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, newKey) +} + +// HandleGetRaw handles a GET request to bzzr://<key> and responds with +// the raw content stored at the given storage key +func (s *Server) HandleGetRaw(w http.ResponseWriter, r *Request) { + key, err := s.api.Resolve(r.uri) + if err != nil { + s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) + return + } + + // if path is set, interpret <key> as a manifest and return the + // raw entry at the given path + if r.uri.Path != "" { + walker, err := s.api.NewManifestWalker(key, nil) + if err != nil { + s.BadRequest(w, r, fmt.Sprintf("%s is not a manifest", key)) return } - if raw { - var reader storage.LazySectionReader - parsedurl, _ := api.Parse(path) - - if parsedurl == path { - key, err := a.Resolve(parsedurl, nameresolver) - if err != nil { - log.Error(fmt.Sprintf("%v", err)) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - reader = a.Retrieve(key) - } else { - var status int - readertmp, _, status, err := a.Get(path, nameresolver) - if err != nil { - http.Error(w, err.Error(), status) - return - } - reader = readertmp + var entry *api.ManifestEntry + walker.Walk(func(e *api.ManifestEntry) error { + // if the entry matches the path, set entry and stop + // the walk + if e.Path == r.uri.Path { + entry = e + // return an error to cancel the walk + return errors.New("found") } - // retrieving content - - quitC := make(chan bool) - size, err := reader.Size(quitC) - if err != nil { - log.Debug(fmt.Sprintf("Could not determine size: %v", err.Error())) - //An error on call to Size means we don't have the root chunk - http.Error(w, err.Error(), http.StatusNotFound) - return + // ignore non-manifest files + if e.ContentType != api.ManifestType { + return nil } - log.Debug(fmt.Sprintf("Reading %d bytes.", size)) - // setting mime type - qv := requestURL.Query() - mimeType := qv.Get("content_type") - if mimeType == "" { - mimeType = rawType + // if the manifest's path is a prefix of the + // requested path, recurse into it by returning + // nil and continuing the walk + if strings.HasPrefix(r.uri.Path, e.Path) { + return nil } - w.Header().Set("Content-Type", mimeType) - http.ServeContent(w, r, uri, forever(), reader) - log.Debug(fmt.Sprintf("Serve raw content '%s' (%d bytes) as '%s'", uri, size, mimeType)) + return api.SkipManifest + }) + if entry == nil { + http.NotFound(w, &r.Request) + return + } + key = storage.Key(common.Hex2Bytes(entry.Hash)) + } - // retrieve path via manifest - } else { - log.Debug(fmt.Sprintf("Structured GET request '%s' received.", uri)) - // add trailing slash, if missing - if rootDocumentUri.MatchString(uri) { - http.Redirect(w, r, path+"/", http.StatusFound) - return + // check the root chunk exists by retrieving the file's size + reader := s.api.Retrieve(key) + if _, err := reader.Size(nil); err != nil { + s.logDebug("key not found %s: %s", key, err) + http.NotFound(w, &r.Request) + return + } + + // allow the request to overwrite the content type using a query + // parameter + contentType := "application/octet-stream" + if typ := r.URL.Query().Get("content_type"); typ != "" { + contentType = typ + } + w.Header().Set("Content-Type", contentType) + + http.ServeContent(w, &r.Request, "", time.Now(), reader) +} + +// HandleGetFiles handles a GET request to bzz:/<manifest> with an Accept +// header of "application/x-tar" and returns a tar stream of all files +// contained in the manifest +func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) { + if r.uri.Path != "" { + s.BadRequest(w, r, "files request cannot contain a path") + return + } + + key, err := s.api.Resolve(r.uri) + if err != nil { + s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) + return + } + + walker, err := s.api.NewManifestWalker(key, nil) + if err != nil { + s.Error(w, r, err) + return + } + + tw := tar.NewWriter(w) + defer tw.Close() + w.Header().Set("Content-Type", "application/x-tar") + w.WriteHeader(http.StatusOK) + + err = walker.Walk(func(entry *api.ManifestEntry) error { + // ignore manifests (walk will recurse into them) + if entry.ContentType == api.ManifestType { + return nil + } + + // retrieve the entry's key and size + reader := s.api.Retrieve(storage.Key(common.Hex2Bytes(entry.Hash))) + size, err := reader.Size(nil) + if err != nil { + return err + } + + // write a tar header for the entry + hdr := &tar.Header{ + Name: entry.Path, + Mode: entry.Mode, + Size: size, + ModTime: entry.ModTime, + Xattrs: map[string]string{ + "user.swarm.content-type": entry.ContentType, + }, + } + if err := tw.WriteHeader(hdr); err != nil { + return err + } + + // copy the file into the tar stream + n, err := io.Copy(tw, io.LimitReader(reader, hdr.Size)) + if err != nil { + return err + } else if n != size { + return fmt.Errorf("error writing %s: expected %d bytes but sent %d", entry.Path, size, n) + } + + return nil + }) + if err != nil { + s.logError("error generating tar stream: %s", err) + } +} + +// HandleGetList handles a GET request to bzz:/<manifest>/<path> which has +// the "list" query parameter set to "true" and returns a list of all files +// contained in <manifest> under <path> grouped into common prefixes using +// "/" as a delimiter +func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) { + // ensure the root path has a trailing slash so that relative URLs work + if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") { + http.Redirect(w, &r.Request, r.URL.Path+"/?list=true", http.StatusMovedPermanently) + return + } + + key, err := s.api.Resolve(r.uri) + if err != nil { + s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) + return + } + + walker, err := s.api.NewManifestWalker(key, nil) + if err != nil { + s.Error(w, r, err) + return + } + + var list api.ManifestList + prefix := r.uri.Path + err = walker.Walk(func(entry *api.ManifestEntry) error { + // handle non-manifest files + if entry.ContentType != api.ManifestType { + // ignore the file if it doesn't have the specified prefix + if !strings.HasPrefix(entry.Path, prefix) { + return nil } - reader, mimeType, status, err := a.Get(path, nameresolver) - if err != nil { - if _, ok := err.(api.ErrResolve); ok { - log.Debug(fmt.Sprintf("%v", err)) - status = http.StatusBadRequest - } else { - log.Debug(fmt.Sprintf("error retrieving '%s': %v", uri, err)) - status = http.StatusNotFound - } - http.Error(w, err.Error(), status) - return + + // if the path after the prefix contains a slash, add a + // common prefix to the list, otherwise add the entry + suffix := strings.TrimPrefix(entry.Path, prefix) + if index := strings.Index(suffix, "/"); index > -1 { + list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1]) + return nil } - // set mime type and status headers - w.Header().Set("Content-Type", mimeType) - if status > 0 { - w.WriteHeader(status) - } else { - status = 200 + if entry.Path == "" { + entry.Path = "/" } - quitC := make(chan bool) - size, err := reader.Size(quitC) - if err != nil { - log.Debug(fmt.Sprintf("Could not determine size: %v", err.Error())) - //An error on call to Size means we don't have the root chunk - http.Error(w, err.Error(), http.StatusNotFound) - return + list.Entries = append(list.Entries, entry) + return nil + } + + // if the manifest's path is a prefix of the specified prefix + // then just recurse into the manifest by returning nil and + // continuing the walk + if strings.HasPrefix(prefix, entry.Path) { + return nil + } + + // if the manifest's path has the specified prefix, then if the + // path after the prefix contains a slash, add a common prefix + // to the list and skip the manifest, otherwise recurse into + // the manifest by returning nil and continuing the walk + if strings.HasPrefix(entry.Path, prefix) { + suffix := strings.TrimPrefix(entry.Path, prefix) + if index := strings.Index(suffix, "/"); index > -1 { + list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1]) + return api.SkipManifest } - log.Debug(fmt.Sprintf("Served '%s' (%d bytes) as '%s' (status code: %v)", uri, size, mimeType, status)) + return nil + } - http.ServeContent(w, r, path, forever(), reader) + // the manifest neither has the prefix or needs recursing in to + // so just skip it + return api.SkipManifest + }) + if err != nil { + s.Error(w, r, err) + return + } + // if the client wants HTML (e.g. a browser) then render the list as a + // HTML index with relative URLs + if strings.Contains(r.Header.Get("Accept"), "text/html") { + w.Header().Set("Content-Type", "text/html") + err := htmlListTemplate.Execute(w, &htmlListData{ + URI: r.uri, + List: &list, + }) + if err != nil { + s.logError("error rendering list HTML: %s", err) } - default: - http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(&list) +} + +// HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds +// with the content of the file at <path> from the given <manifest> +func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) { + key, err := s.api.Resolve(r.uri) + if err != nil { + s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err)) + return + } + + reader, contentType, _, err := s.api.Get(key, r.uri.Path) + if err != nil { + s.Error(w, r, err) + return + } + + // check the root chunk exists by retrieving the file's size + if _, err := reader.Size(nil); err != nil { + s.logDebug("file not found %s: %s", r.uri, err) + http.NotFound(w, &r.Request) + return } + + w.Header().Set("Content-Type", contentType) + + http.ServeContent(w, &r.Request, "", time.Now(), reader) } -func (self *sequentialReader) ReadAt(target []byte, off int64) (n int, err error) { - self.lock.Lock() - // assert self.pos <= off - if self.pos > off { - log.Error(fmt.Sprintf("non-sequential read attempted from sequentialReader; %d > %d", self.pos, off)) - panic("Non-sequential read attempt") - } - if self.pos != off { - log.Debug(fmt.Sprintf("deferred read in POST at position %d, offset %d.", self.pos, off)) - wait := make(chan bool) - self.ahead[off] = wait - self.lock.Unlock() - if <-wait { - // failed read behind - n = 0 - err = io.ErrUnexpectedEOF +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.logDebug("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, r.URL.Host, r.URL.Path, r.Referer(), r.Header.Get("Accept")) + + uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/")) + if err != nil { + s.logError("Invalid URI %q: %s", r.URL.Path, err) + http.Error(w, fmt.Sprintf("Invalid bzz URI: %s", err), http.StatusBadRequest) + return + } + s.logDebug("%s request received for %s", r.Method, uri) + + req := &Request{Request: *r, uri: uri} + switch r.Method { + case "POST": + if uri.Raw() { + s.HandlePostRaw(w, req) + } else { + s.HandlePostFiles(w, req) + } + + case "PUT": + // DEPRECATED: + // clients should send a POST request (the request creates a + // new manifest leaving the existing one intact, so it isn't + // strictly a traditional PUT request which replaces content + // at a URI, and POST is more ubiquitous) + if uri.Raw() { + http.Error(w, fmt.Sprintf("No PUT to %s allowed.", uri), http.StatusBadRequest) return + } else { + s.HandlePostFiles(w, req) } - self.lock.Lock() - } - localPos := 0 - for localPos < len(target) { - n, err = self.reader.Read(target[localPos:]) - localPos += n - log.Debug(fmt.Sprintf("Read %d bytes into buffer size %d from POST, error %v.", n, len(target), err)) - if err != nil { - log.Debug(fmt.Sprintf("POST stream's reading terminated with %v.", err)) - for i := range self.ahead { - self.ahead[i] <- true - delete(self.ahead, i) - } - self.lock.Unlock() - return localPos, err + + case "DELETE": + if uri.Raw() { + http.Error(w, fmt.Sprintf("No DELETE to %s allowed.", uri), http.StatusBadRequest) + return + } + s.HandleDelete(w, req) + + case "GET": + if uri.Raw() { + s.HandleGetRaw(w, req) + return } - self.pos += int64(n) + + if r.Header.Get("Accept") == "application/x-tar" { + s.HandleGetFiles(w, req) + return + } + + if r.URL.Query().Get("list") == "true" { + s.HandleGetList(w, req) + return + } + + s.HandleGetFile(w, req) + + default: + http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed) + } - wait := self.ahead[self.pos] - if wait != nil { - log.Debug(fmt.Sprintf("deferred read in POST at position %d triggered.", self.pos)) - delete(self.ahead, self.pos) - close(wait) +} + +func (s *Server) updateManifest(key storage.Key, update func(mw *api.ManifestWriter) error) (storage.Key, error) { + mw, err := s.api.NewManifestWriter(key, nil) + if err != nil { + return nil, err } - self.lock.Unlock() - return localPos, err + + if err := update(mw); err != nil { + return nil, err + } + + key, err = mw.Store() + if err != nil { + return nil, err + } + s.logDebug("generated manifest %s", key) + return key, nil +} + +func (s *Server) logDebug(format string, v ...interface{}) { + log.Debug(fmt.Sprintf("[BZZ] HTTP: "+format, v...)) +} + +func (s *Server) logError(format string, v ...interface{}) { + log.Error(fmt.Sprintf("[BZZ] HTTP: "+format, v...)) +} + +func (s *Server) BadRequest(w http.ResponseWriter, r *Request, reason string) { + s.logDebug("bad request %s %s: %s", r.Method, r.uri, reason) + http.Error(w, reason, http.StatusBadRequest) +} + +func (s *Server) Error(w http.ResponseWriter, r *Request, err error) { + s.logError("error serving %s %s: %s", r.Method, r.uri, err) + http.Error(w, err.Error(), http.StatusInternalServerError) } diff --git a/swarm/api/http/server_test.go b/swarm/api/http/server_test.go index 88b49b9a5..ceb8db75b 100644 --- a/swarm/api/http/server_test.go +++ b/swarm/api/http/server_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -package http +package http_test import ( "bytes" @@ -22,19 +22,16 @@ import ( "net/http" "sync" "testing" - "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/storage" + "github.com/ethereum/go-ethereum/swarm/testutil" ) func TestBzzrGetPath(t *testing.T) { var err error - maxproxyattempts := 3 - testmanifest := []string{ `{"entries":[{"path":"a/","hash":"674af7073604ebfc0282a4ab21e5ef1a3c22913866879ebc0816f8a89896b2ed","contentType":"application/bzz-manifest+json","status":0}]}`, `{"entries":[{"path":"a","hash":"011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce","contentType":"","status":0},{"path":"b/","hash":"0a87b1c3e4bf013686cdf107ec58590f2004610ee58cc2240f26939f691215f5","contentType":"application/bzz-manifest+json","status":0}]}`, @@ -43,8 +40,8 @@ func TestBzzrGetPath(t *testing.T) { testrequests := make(map[string]int) testrequests["/"] = 0 - testrequests["/a"] = 1 - testrequests["/a/b"] = 2 + testrequests["/a/"] = 1 + testrequests["/a/b/"] = 2 testrequests["/x"] = 0 testrequests[""] = 0 @@ -54,61 +51,30 @@ func TestBzzrGetPath(t *testing.T) { key := [3]storage.Key{} - dir, _ := ioutil.TempDir("", "bzz-storage-test") - - storeparams := &storage.StoreParams{ - ChunkDbPath: dir, - DbCapacity: 5000000, - CacheCapacity: 5000, - Radius: 0, - } - - localStore, err := storage.NewLocalStore(storage.MakeHashFunc("SHA3"), storeparams) - if err != nil { - t.Fatal(err) - } - chunker := storage.NewTreeChunker(storage.NewChunkerParams()) - dpa := &storage.DPA{ - Chunker: chunker, - ChunkStore: localStore, - } - dpa.Start() - defer dpa.Stop() + srv := testutil.NewTestSwarmServer(t) + defer srv.Close() wg := &sync.WaitGroup{} for i, mf := range testmanifest { reader[i] = bytes.NewReader([]byte(mf)) - key[i], err = dpa.Store(reader[i], int64(len(mf)), wg, nil) + key[i], err = srv.Dpa.Store(reader[i], int64(len(mf)), wg, nil) if err != nil { t.Fatal(err) } wg.Wait() } - a := api.NewApi(dpa, nil) - - /// \todo iterate port numbers up if fail - StartHttpServer(a, &Server{Addr: "127.0.0.1:8504", CorsString: ""}) - // how to wait for ListenAndServe to have initialized? This is pretty cruuuude - // if we fix it we don't need maxproxyattempts anymore either - time.Sleep(1000 * time.Millisecond) - for i := 0; i <= maxproxyattempts; i++ { - _, err := http.Get("http://127.0.0.1:8504/bzzr:/" + common.ToHex(key[0])[2:] + "/a") - if i == maxproxyattempts { - t.Fatalf("Failed to connect to proxy after %v attempts: %v", i, err) - } else if err != nil { - time.Sleep(100 * time.Millisecond) - continue - } - break + _, err = http.Get(srv.URL + "/bzzr:/" + common.ToHex(key[0])[2:] + "/a") + if err != nil { + t.Fatalf("Failed to connect to proxy: %v", err) } for k, v := range testrequests { var resp *http.Response var respbody []byte - url := "http://127.0.0.1:8504/bzzr:/" + url := srv.URL + "/bzzr:/" if k[:] != "" { url += common.ToHex(key[0])[2:] + "/" + k[1:] + "?content_type=text/plain" } @@ -133,4 +99,32 @@ func TestBzzrGetPath(t *testing.T) { } } + nonhashtests := []string{ + srv.URL + "/bzz:/name", + srv.URL + "/bzzi:/nonhash", + srv.URL + "/bzzr:/nonhash", + } + + nonhashresponses := []string{ + "error resolving name: 'name' does not resolve: no DNS to resolve name but is not a content hash\n", + "error resolving nonhash: 'nonhash' is not a content hash\n", + "error resolving nonhash: 'nonhash' does not resolve: no DNS to resolve name but is not a content hash\n", + } + + for i, url := range nonhashtests { + var resp *http.Response + var respbody []byte + + resp, err = http.Get(url) + + if err != nil { + t.Fatalf("Request failed: %v", err) + } + defer resp.Body.Close() + respbody, err = ioutil.ReadAll(resp.Body) + if string(respbody) != nonhashresponses[i] { + t.Fatalf("Non-Hash response body does not match, expected: %v, got: %v", nonhashresponses[i], string(respbody)) + } + } + } diff --git a/swarm/api/http/templates.go b/swarm/api/http/templates.go new file mode 100644 index 000000000..c3ef8c0f4 --- /dev/null +++ b/swarm/api/http/templates.go @@ -0,0 +1,71 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package http + +import ( + "html/template" + "path" + + "github.com/ethereum/go-ethereum/swarm/api" +) + +type htmlListData struct { + URI *api.URI + List *api.ManifestList +} + +var htmlListTemplate = template.Must(template.New("html-list").Funcs(template.FuncMap{"basename": path.Base}).Parse(` +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Swarm index of {{ .URI }}</title> +</head> + +<body> + <h1>Swarm index of {{ .URI }}</h1> + <hr> + <table> + <thead> + <tr> + <th>Path</th> + <th>Type</th> + <th>Size</th> + </tr> + </thead> + + <tbody> + {{ range .List.CommonPrefixes }} + <tr> + <td><a href="{{ basename . }}/?list=true">{{ basename . }}/</a></td> + <td>DIR</td> + <td>-</td> + </tr> + {{ end }} + + {{ range .List.Entries }} + <tr> + <td><a href="{{ basename .Path }}">{{ basename .Path }}</a></td> + <td>{{ .ContentType }}</td> + <td>{{ .Size }}</td> + </tr> + {{ end }} + </table> + <hr> +</body> +`[1:])) diff --git a/swarm/api/manifest.go b/swarm/api/manifest.go index 199f259e1..dbaaf4bff 100644 --- a/swarm/api/manifest.go +++ b/swarm/api/manifest.go @@ -19,8 +19,11 @@ package api import ( "bytes" "encoding/json" + "errors" "fmt" + "io" "sync" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -28,25 +31,152 @@ import ( ) const ( - manifestType = "application/bzz-manifest+json" + ManifestType = "application/bzz-manifest+json" ) +// Manifest represents a swarm manifest +type Manifest struct { + Entries []ManifestEntry `json:"entries,omitempty"` +} + +// ManifestEntry represents an entry in a swarm manifest +type ManifestEntry struct { + Hash string `json:"hash,omitempty"` + Path string `json:"path,omitempty"` + ContentType string `json:"contentType,omitempty"` + Mode int64 `json:"mode,omitempty"` + Size int64 `json:"size,omitempty"` + ModTime time.Time `json:"mod_time,omitempty"` + Status int `json:"status,omitempty"` +} + +// ManifestList represents the result of listing files in a manifest +type ManifestList struct { + CommonPrefixes []string `json:"common_prefixes,omitempty"` + Entries []*ManifestEntry `json:"entries,omitempty"` +} + +// NewManifest creates and stores a new, empty manifest +func (a *Api) NewManifest() (storage.Key, error) { + var manifest Manifest + data, err := json.Marshal(&manifest) + if err != nil { + return nil, err + } + return a.Store(bytes.NewReader(data), int64(len(data)), nil) +} + +// ManifestWriter is used to add and remove entries from an underlying manifest +type ManifestWriter struct { + api *Api + trie *manifestTrie + quitC chan bool +} + +func (a *Api) NewManifestWriter(key storage.Key, quitC chan bool) (*ManifestWriter, error) { + trie, err := loadManifest(a.dpa, key, quitC) + if err != nil { + return nil, fmt.Errorf("error loading manifest %s: %s", key, err) + } + return &ManifestWriter{a, trie, quitC}, nil +} + +// AddEntry stores the given data and adds the resulting key to the manifest +func (m *ManifestWriter) AddEntry(data io.Reader, e *ManifestEntry) (storage.Key, error) { + key, err := m.api.Store(data, e.Size, nil) + if err != nil { + return nil, err + } + entry := newManifestTrieEntry(e, nil) + entry.Hash = key.String() + m.trie.addEntry(entry, m.quitC) + return key, nil +} + +// RemoveEntry removes the given path from the manifest +func (m *ManifestWriter) RemoveEntry(path string) error { + m.trie.deleteEntry(path, m.quitC) + return nil +} + +// Store stores the manifest, returning the resulting storage key +func (m *ManifestWriter) Store() (storage.Key, error) { + return m.trie.hash, m.trie.recalcAndStore() +} + +// ManifestWalker is used to recursively walk the entries in the manifest and +// all of its submanifests +type ManifestWalker struct { + api *Api + trie *manifestTrie + quitC chan bool +} + +func (a *Api) NewManifestWalker(key storage.Key, quitC chan bool) (*ManifestWalker, error) { + trie, err := loadManifest(a.dpa, key, quitC) + if err != nil { + return nil, fmt.Errorf("error loading manifest %s: %s", key, err) + } + return &ManifestWalker{a, trie, quitC}, nil +} + +// SkipManifest is used as a return value from WalkFn to indicate that the +// manifest should be skipped +var SkipManifest = errors.New("skip this manifest") + +// WalkFn is the type of function called for each entry visited by a recursive +// manifest walk +type WalkFn func(entry *ManifestEntry) error + +// Walk recursively walks the manifest calling walkFn for each entry in the +// manifest, including submanifests +func (m *ManifestWalker) Walk(walkFn WalkFn) error { + return m.walk(m.trie, "", walkFn) +} + +func (m *ManifestWalker) walk(trie *manifestTrie, prefix string, walkFn WalkFn) error { + for _, entry := range trie.entries { + if entry == nil { + continue + } + entry.Path = prefix + entry.Path + err := walkFn(&entry.ManifestEntry) + if err != nil { + if entry.ContentType == ManifestType && err == SkipManifest { + continue + } + return err + } + if entry.ContentType != ManifestType { + continue + } + if err := trie.loadSubTrie(entry, nil); err != nil { + return err + } + if err := m.walk(entry.subtrie, entry.Path, walkFn); err != nil { + return err + } + } + return nil +} + type manifestTrie struct { dpa *storage.DPA - entries [257]*manifestTrieEntry // indexed by first character of path, entries[256] is the empty path entry + entries [257]*manifestTrieEntry // indexed by first character of basePath, entries[256] is the empty basePath entry hash storage.Key // if hash != nil, it is stored } -type manifestJSON struct { - Entries []*manifestTrieEntry `json:"entries"` +func newManifestTrieEntry(entry *ManifestEntry, subtrie *manifestTrie) *manifestTrieEntry { + return &manifestTrieEntry{ + ManifestEntry: *entry, + subtrie: subtrie, + } } type manifestTrieEntry struct { - Path string `json:"path"` - Hash string `json:"hash"` // for manifest content type, empty until subtrie is evaluated - ContentType string `json:"contentType"` - Status int `json:"status"` - subtrie *manifestTrie + ManifestEntry + + subtrie *manifestTrie } func loadManifest(dpa *storage.DPA, hash storage.Key, quitC chan bool) (trie *manifestTrie, err error) { // non-recursive, subtrees are downloaded on-demand @@ -77,7 +207,9 @@ func readManifest(manifestReader storage.LazySectionReader, hash storage.Key, dp } log.Trace(fmt.Sprintf("Manifest %v retrieved", hash.Log())) - man := manifestJSON{} + var man struct { + Entries []*manifestTrieEntry `json:"entries"` + } err = json.Unmarshal(manifestData, &man) if err != nil { err = fmt.Errorf("Manifest %v is malformed: %v", hash.Log(), err) @@ -116,7 +248,7 @@ func (self *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) { cpl++ } - if (oldentry.ContentType == manifestType) && (cpl == len(oldentry.Path)) { + if (oldentry.ContentType == ManifestType) && (cpl == len(oldentry.Path)) { if self.loadSubTrie(oldentry, quitC) != nil { return } @@ -136,12 +268,10 @@ func (self *manifestTrie) addEntry(entry *manifestTrieEntry, quitC chan bool) { subtrie.addEntry(entry, quitC) subtrie.addEntry(oldentry, quitC) - self.entries[b] = &manifestTrieEntry{ + self.entries[b] = newManifestTrieEntry(&ManifestEntry{ Path: commonPrefix, - Hash: "", - ContentType: manifestType, - subtrie: subtrie, - } + ContentType: ManifestType, + }, subtrie) } func (self *manifestTrie) getCountLast() (cnt int, entry *manifestTrieEntry) { @@ -173,7 +303,7 @@ func (self *manifestTrie) deleteEntry(path string, quitC chan bool) { } epl := len(entry.Path) - if (entry.ContentType == manifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { + if (entry.ContentType == ManifestType) && (len(path) >= epl) && (path[:epl] == entry.Path) { if self.loadSubTrie(entry, quitC) != nil { return } @@ -198,7 +328,7 @@ func (self *manifestTrie) recalcAndStore() error { var buffer bytes.Buffer buffer.WriteString(`{"entries":[`) - list := &manifestJSON{} + list := &Manifest{} for _, entry := range self.entries { if entry != nil { if entry.Hash == "" { // TODO: paralellize @@ -208,8 +338,9 @@ func (self *manifestTrie) recalcAndStore() error { } entry.Hash = entry.subtrie.hash.String() } - list.Entries = append(list.Entries, entry) + list.Entries = append(list.Entries, entry.ManifestEntry) } + } manifest, err := json.Marshal(list) @@ -254,7 +385,7 @@ func (self *manifestTrie) listWithPrefixInt(prefix, rp string, quitC chan bool, entry := self.entries[i] if entry != nil { epl := len(entry.Path) - if entry.ContentType == manifestType { + if entry.ContentType == ManifestType { l := plen if epl < l { l = epl @@ -300,7 +431,7 @@ func (self *manifestTrie) findPrefixOf(path string, quitC chan bool) (entry *man log.Trace(fmt.Sprintf("path = %v entry.Path = %v epl = %v", path, entry.Path, epl)) if (len(path) >= epl) && (path[:epl] == entry.Path) { log.Trace(fmt.Sprintf("entry.ContentType = %v", entry.ContentType)) - if entry.ContentType == manifestType { + if entry.ContentType == ManifestType { err := self.loadSubTrie(entry, quitC) if err != nil { return nil, 0 diff --git a/swarm/api/storage.go b/swarm/api/storage.go index 31b484675..0e3abecfe 100644 --- a/swarm/api/storage.go +++ b/swarm/api/storage.go @@ -16,6 +16,8 @@ package api +import "path" + type Response struct { MimeType string Status int @@ -25,6 +27,8 @@ type Response struct { } // implements a service +// +// DEPRECATED: Use the HTTP API instead type Storage struct { api *Api } @@ -35,8 +39,14 @@ func NewStorage(api *Api) *Storage { // Put uploads the content to the swarm with a simple manifest speficying // its content type +// +// DEPRECATED: Use the HTTP API instead func (self *Storage) Put(content, contentType string) (string, error) { - return self.api.Put(content, contentType) + key, err := self.api.Put(content, contentType) + if err != nil { + return "", err + } + return key.String(), err } // Get retrieves the content from bzzpath and reads the response in full @@ -45,8 +55,18 @@ func (self *Storage) Put(content, contentType string) (string, error) { // NOTE: if error is non-nil, sResponse may still have partial content // the actual size of which is given in len(resp.Content), while the expected // size is resp.Size +// +// DEPRECATED: Use the HTTP API instead func (self *Storage) Get(bzzpath string) (*Response, error) { - reader, mimeType, status, err := self.api.Get(bzzpath, true) + uri, err := Parse(path.Join("bzz:/", bzzpath)) + if err != nil { + return nil, err + } + key, err := self.api.Resolve(uri) + if err != nil { + return nil, err + } + reader, mimeType, status, err := self.api.Get(key, uri.Path) if err != nil { return nil, err } @@ -63,8 +83,22 @@ func (self *Storage) Get(bzzpath string) (*Response, error) { return &Response{mimeType, status, expsize, string(body[:size])}, err } -// Modify(rootHash, path, contentHash, contentType) takes th e manifest trie rooted in rootHash, +// Modify(rootHash, basePath, contentHash, contentType) takes th e manifest trie rooted in rootHash, // and merge on to it. creating an entry w conentType (mime) +// +// DEPRECATED: Use the HTTP API instead func (self *Storage) Modify(rootHash, path, contentHash, contentType string) (newRootHash string, err error) { - return self.api.Modify(rootHash+"/"+path, contentHash, contentType, true) + uri, err := Parse("bzz:/" + rootHash) + if err != nil { + return "", err + } + key, err := self.api.Resolve(uri) + if err != nil { + return "", err + } + key, err = self.api.Modify(key, path, contentHash, contentType) + if err != nil { + return "", err + } + return key.String(), nil } diff --git a/swarm/api/storage_test.go b/swarm/api/storage_test.go index 72caf52df..d260dd61d 100644 --- a/swarm/api/storage_test.go +++ b/swarm/api/storage_test.go @@ -36,7 +36,7 @@ func TestStoragePutGet(t *testing.T) { t.Fatalf("unexpected error: %v", err) } // to check put against the Api#Get - resp0 := testGet(t, api.api, bzzhash) + resp0 := testGet(t, api.api, bzzhash, "") checkResponse(t, resp0, exp) // check storage#Get diff --git a/swarm/api/uri.go b/swarm/api/uri.go new file mode 100644 index 000000000..68ce04835 --- /dev/null +++ b/swarm/api/uri.go @@ -0,0 +1,96 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package api + +import ( + "fmt" + "net/url" + "strings" +) + +// URI is a reference to content stored in swarm. +type URI struct { + // Scheme has one of the following values: + // + // * bzz - an entry in a swarm manifest + // * bzzr - raw swarm content + // * bzzi - immutable URI of an entry in a swarm manifest + // (address is not resolved) + Scheme string + + // Addr is either a hexadecimal storage key or it an address which + // resolves to a storage key + Addr string + + // Path is the path to the content within a swarm manifest + Path string +} + +// Parse parses rawuri into a URI struct, where rawuri is expected to have one +// of the following formats: +// +// * <scheme>:/ +// * <scheme>:/<addr> +// * <scheme>:/<addr>/<path> +// * <scheme>:// +// * <scheme>://<addr> +// * <scheme>://<addr>/<path> +// +// with scheme one of bzz, bzzr or bzzi +func Parse(rawuri string) (*URI, error) { + u, err := url.Parse(rawuri) + if err != nil { + return nil, err + } + uri := &URI{Scheme: u.Scheme} + + // check the scheme is valid + switch uri.Scheme { + case "bzz", "bzzi", "bzzr": + default: + return nil, fmt.Errorf("unknown scheme %q", u.Scheme) + } + + // handle URIs like bzz://<addr>/<path> where the addr and path + // have already been split by url.Parse + if u.Host != "" { + uri.Addr = u.Host + uri.Path = strings.TrimLeft(u.Path, "/") + return uri, nil + } + + // URI is like bzz:/<addr>/<path> so split the addr and path from + // the raw path (which will be /<addr>/<path>) + parts := strings.SplitN(strings.TrimLeft(u.Path, "/"), "/", 2) + uri.Addr = parts[0] + if len(parts) == 2 { + uri.Path = parts[1] + } + return uri, nil +} + +func (u *URI) Raw() bool { + return u.Scheme == "bzzr" +} + +func (u *URI) Immutable() bool { + return u.Scheme == "bzzi" +} + +func (u *URI) String() string { + return u.Scheme + ":/" + u.Addr + "/" + u.Path +} diff --git a/swarm/api/uri_test.go b/swarm/api/uri_test.go new file mode 100644 index 000000000..dcb5fbbff --- /dev/null +++ b/swarm/api/uri_test.go @@ -0,0 +1,120 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package api + +import ( + "reflect" + "testing" +) + +func TestParseURI(t *testing.T) { + type test struct { + uri string + expectURI *URI + expectErr bool + expectRaw bool + expectImmutable bool + } + tests := []test{ + { + uri: "", + expectErr: true, + }, + { + uri: "foo", + expectErr: true, + }, + { + uri: "bzz", + expectErr: true, + }, + { + uri: "bzz:", + expectURI: &URI{Scheme: "bzz"}, + }, + { + uri: "bzzi:", + expectURI: &URI{Scheme: "bzzi"}, + expectImmutable: true, + }, + { + uri: "bzzr:", + expectURI: &URI{Scheme: "bzzr"}, + expectRaw: true, + }, + { + uri: "bzz:/", + expectURI: &URI{Scheme: "bzz"}, + }, + { + uri: "bzz:/abc123", + expectURI: &URI{Scheme: "bzz", Addr: "abc123"}, + }, + { + uri: "bzz:/abc123/path/to/entry", + expectURI: &URI{Scheme: "bzz", Addr: "abc123", Path: "path/to/entry"}, + }, + { + uri: "bzzr:/", + expectURI: &URI{Scheme: "bzzr"}, + expectRaw: true, + }, + { + uri: "bzzr:/abc123", + expectURI: &URI{Scheme: "bzzr", Addr: "abc123"}, + expectRaw: true, + }, + { + uri: "bzzr:/abc123/path/to/entry", + expectURI: &URI{Scheme: "bzzr", Addr: "abc123", Path: "path/to/entry"}, + expectRaw: true, + }, + { + uri: "bzz://", + expectURI: &URI{Scheme: "bzz"}, + }, + { + uri: "bzz://abc123", + expectURI: &URI{Scheme: "bzz", Addr: "abc123"}, + }, + { + uri: "bzz://abc123/path/to/entry", + expectURI: &URI{Scheme: "bzz", Addr: "abc123", Path: "path/to/entry"}, + }, + } + for _, x := range tests { + actual, err := Parse(x.uri) + if x.expectErr { + if err == nil { + t.Fatalf("expected %s to error", x.uri) + } + continue + } + if err != nil { + t.Fatalf("error parsing %s: %s", x.uri, err) + } + if !reflect.DeepEqual(actual, x.expectURI) { + t.Fatalf("expected %s to return %#v, got %#v", x.uri, x.expectURI, actual) + } + if actual.Raw() != x.expectRaw { + t.Fatalf("expected %s raw to be %t, got %t", x.uri, x.expectRaw, actual.Raw()) + } + if actual.Immutable() != x.expectImmutable { + t.Fatalf("expected %s immutable to be %t, got %t", x.uri, x.expectImmutable, actual.Immutable()) + } + } +} diff --git a/swarm/fuse/fuse_dir.go b/swarm/fuse/fuse_dir.go new file mode 100644 index 000000000..91b236ae8 --- /dev/null +++ b/swarm/fuse/fuse_dir.go @@ -0,0 +1,155 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// +build linux darwin freebsd + +package fuse + +import ( + "bazil.org/fuse" + "bazil.org/fuse/fs" + "golang.org/x/net/context" + "os" + "path/filepath" + "sync" +) + +var ( + _ fs.Node = (*SwarmDir)(nil) + _ fs.NodeRequestLookuper = (*SwarmDir)(nil) + _ fs.HandleReadDirAller = (*SwarmDir)(nil) + _ fs.NodeCreater = (*SwarmDir)(nil) + _ fs.NodeRemover = (*SwarmDir)(nil) + _ fs.NodeMkdirer = (*SwarmDir)(nil) +) + +type SwarmDir struct { + inode uint64 + name string + path string + directories []*SwarmDir + files []*SwarmFile + + mountInfo *MountInfo + lock *sync.RWMutex +} + +func NewSwarmDir(fullpath string, minfo *MountInfo) *SwarmDir { + newdir := &SwarmDir{ + inode: NewInode(), + name: filepath.Base(fullpath), + path: fullpath, + directories: []*SwarmDir{}, + files: []*SwarmFile{}, + mountInfo: minfo, + lock: &sync.RWMutex{}, + } + return newdir +} + +func (sd *SwarmDir) Attr(ctx context.Context, a *fuse.Attr) error { + a.Inode = sd.inode + a.Mode = os.ModeDir | 0700 + a.Uid = uint32(os.Getuid()) + a.Gid = uint32(os.Getegid()) + return nil +} + +func (sd *SwarmDir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) { + + for _, n := range sd.files { + if n.name == req.Name { + return n, nil + } + } + for _, n := range sd.directories { + if n.name == req.Name { + return n, nil + } + } + return nil, fuse.ENOENT +} + +func (sd *SwarmDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { + var children []fuse.Dirent + for _, file := range sd.files { + children = append(children, fuse.Dirent{Inode: file.inode, Type: fuse.DT_File, Name: file.name}) + } + for _, dir := range sd.directories { + children = append(children, fuse.Dirent{Inode: dir.inode, Type: fuse.DT_Dir, Name: dir.name}) + } + return children, nil +} + +func (sd *SwarmDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) { + + newFile := NewSwarmFile(sd.path, req.Name, sd.mountInfo) + newFile.fileSize = 0 // 0 means, file is not in swarm yet and it is just created + + sd.lock.Lock() + defer sd.lock.Unlock() + sd.files = append(sd.files, newFile) + + return newFile, newFile, nil +} + +func (sd *SwarmDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error { + + if req.Dir && sd.directories != nil { + newDirs := []*SwarmDir{} + for _, dir := range sd.directories { + if dir.name == req.Name { + removeDirectoryFromSwarm(dir) + } else { + newDirs = append(newDirs, dir) + } + } + if len(sd.directories) > len(newDirs) { + sd.lock.Lock() + defer sd.lock.Unlock() + sd.directories = newDirs + } + return nil + } else if !req.Dir && sd.files != nil { + newFiles := []*SwarmFile{} + for _, f := range sd.files { + if f.name == req.Name { + removeFileFromSwarm(f) + } else { + newFiles = append(newFiles, f) + } + } + if len(sd.files) > len(newFiles) { + sd.lock.Lock() + defer sd.lock.Unlock() + sd.files = newFiles + } + return nil + } + return fuse.ENOENT +} + +func (sd *SwarmDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) { + + newDir := NewSwarmDir(req.Name, sd.mountInfo) + + sd.lock.Lock() + defer sd.lock.Unlock() + sd.directories = append(sd.directories, newDir) + + return newDir, nil + +} diff --git a/swarm/fuse/fuse_file.go b/swarm/fuse/fuse_file.go new file mode 100644 index 000000000..0cb59dfb3 --- /dev/null +++ b/swarm/fuse/fuse_file.go @@ -0,0 +1,144 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// +build linux darwin freebsd + +package fuse + +import ( + "bazil.org/fuse" + "bazil.org/fuse/fs" + "errors" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/swarm/storage" + "golang.org/x/net/context" + "io" + "os" + "sync" +) + +const ( + MaxAppendFileSize = 10485760 // 10Mb +) + +var ( + errInvalidOffset = errors.New("Invalid offset during write") + errFileSizeMaxLimixReached = errors.New("File size exceeded max limit") +) + +var ( + _ fs.Node = (*SwarmFile)(nil) + _ fs.HandleReader = (*SwarmFile)(nil) + _ fs.HandleWriter = (*SwarmFile)(nil) +) + +type SwarmFile struct { + inode uint64 + name string + path string + key storage.Key + fileSize int64 + reader storage.LazySectionReader + + mountInfo *MountInfo + lock *sync.RWMutex +} + +func NewSwarmFile(path, fname string, minfo *MountInfo) *SwarmFile { + newFile := &SwarmFile{ + inode: NewInode(), + name: fname, + path: path, + key: nil, + fileSize: -1, // -1 means , file already exists in swarm and you need to just get the size from swarm + reader: nil, + + mountInfo: minfo, + lock: &sync.RWMutex{}, + } + return newFile +} + +func (file *SwarmFile) Attr(ctx context.Context, a *fuse.Attr) error { + + a.Inode = file.inode + //TODO: need to get permission as argument + a.Mode = 0700 + a.Uid = uint32(os.Getuid()) + a.Gid = uint32(os.Getegid()) + + if file.fileSize == -1 { + reader := file.mountInfo.swarmApi.Retrieve(file.key) + quitC := make(chan bool) + size, err := reader.Size(quitC) + if err != nil { + log.Warn("Couldnt get size of file %s : %v", file.path, err) + } + file.fileSize = int64(size) + } + a.Size = uint64(file.fileSize) + return nil +} + +func (sf *SwarmFile) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { + + sf.lock.RLock() + defer sf.lock.RUnlock() + if sf.reader == nil { + sf.reader = sf.mountInfo.swarmApi.Retrieve(sf.key) + } + buf := make([]byte, req.Size) + n, err := sf.reader.ReadAt(buf, req.Offset) + if err == io.ErrUnexpectedEOF || err == io.EOF { + err = nil + } + resp.Data = buf[:n] + sf.reader = nil + return err + +} + +func (sf *SwarmFile) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { + + if sf.fileSize == 0 && req.Offset == 0 { + + // A new file is created + err := addFileToSwarm(sf, req.Data, len(req.Data)) + if err != nil { + return err + } + resp.Size = len(req.Data) + + } else if req.Offset <= sf.fileSize { + + totalSize := sf.fileSize + int64(len(req.Data)) + if totalSize > MaxAppendFileSize { + log.Warn("Append file size reached (%v) : (%v)", sf.fileSize, len(req.Data)) + return errFileSizeMaxLimixReached + } + + err := appendToExistingFileInSwarm(sf, req.Data, req.Offset, int64(len(req.Data))) + if err != nil { + return err + } + resp.Size = int(sf.fileSize) + } else { + log.Warn("Invalid write request size(%v) : off(%v)", sf.fileSize, req.Offset) + return errInvalidOffset + } + + return nil +} diff --git a/mobile/big_go1.7.go b/swarm/fuse/fuse_root.go index 0447e1f66..b2262d1c5 100644 --- a/mobile/big_go1.7.go +++ b/swarm/fuse/fuse_root.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-ethereum Authors +// Copyright 2017 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -14,13 +14,22 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. -// Contains the wrappers from the math/big package that require Go 1.7 and above. +// +build linux darwin freebsd -// +build go1.7 +package fuse -package geth +import ( + "bazil.org/fuse/fs" +) -// GetString returns the value of x as a formatted string in some number base. -func (bi *BigInt) GetString(base int) string { - return bi.bigint.Text(base) +var ( + _ fs.Node = (*SwarmDir)(nil) +) + +type SwarmRoot struct { + root *SwarmDir +} + +func (filesystem *SwarmRoot) Root() (fs.Node, error) { + return filesystem.root, nil } diff --git a/swarm/fuse/swarmfs.go b/swarm/fuse/swarmfs.go new file mode 100644 index 000000000..2493bdab1 --- /dev/null +++ b/swarm/fuse/swarmfs.go @@ -0,0 +1,64 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package fuse + +import ( + "github.com/ethereum/go-ethereum/swarm/api" + "sync" + "time" +) + +const ( + Swarmfs_Version = "0.1" + mountTimeout = time.Second * 5 + unmountTimeout = time.Second * 10 + maxFuseMounts = 5 +) + +var ( + swarmfs *SwarmFS // Swarm file system singleton + swarmfsLock sync.Once + + inode uint64 = 1 // global inode + inodeLock sync.RWMutex +) + +type SwarmFS struct { + swarmApi *api.Api + activeMounts map[string]*MountInfo + swarmFsLock *sync.RWMutex +} + +func NewSwarmFS(api *api.Api) *SwarmFS { + swarmfsLock.Do(func() { + swarmfs = &SwarmFS{ + swarmApi: api, + swarmFsLock: &sync.RWMutex{}, + activeMounts: map[string]*MountInfo{}, + } + }) + return swarmfs + +} + +// Inode numbers need to be unique, they are used for caching inside fuse +func NewInode() uint64 { + inodeLock.Lock() + defer inodeLock.Unlock() + inode += 1 + return inode +} diff --git a/swarm/fuse/swarmfs_fallback.go b/swarm/fuse/swarmfs_fallback.go new file mode 100644 index 000000000..4864c8689 --- /dev/null +++ b/swarm/fuse/swarmfs_fallback.go @@ -0,0 +1,51 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// +build !linux,!darwin,!freebsd + +package fuse + +import ( + "errors" +) + +var errNoFUSE = errors.New("FUSE is not supported on this platform") + +func isFUSEUnsupportedError(err error) bool { + return err == errNoFUSE +} + +type MountInfo struct { + MountPoint string + StartManifest string + LatestManifest string +} + +func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) { + return nil, errNoFUSE +} + +func (self *SwarmFS) Unmount(mountpoint string) (bool, error) { + return false, errNoFUSE +} + +func (self *SwarmFS) Listmounts() ([]*MountInfo, error) { + return nil, errNoFUSE +} + +func (self *SwarmFS) Stop() error { + return nil +} diff --git a/swarm/fuse/swarmfs_test.go b/swarm/fuse/swarmfs_test.go new file mode 100644 index 000000000..f307b38ea --- /dev/null +++ b/swarm/fuse/swarmfs_test.go @@ -0,0 +1,897 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// +build linux darwin freebsd + +package fuse + +import ( + "bytes" + "crypto/rand" + "github.com/ethereum/go-ethereum/swarm/api" + "github.com/ethereum/go-ethereum/swarm/storage" + "io" + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +type fileInfo struct { + perm uint64 + uid int + gid int + contents []byte +} + +func testFuseFileSystem(t *testing.T, f func(*api.Api)) { + + datadir, err := ioutil.TempDir("", "fuse") + if err != nil { + t.Fatalf("unable to create temp dir: %v", err) + } + os.RemoveAll(datadir) + + dpa, err := storage.NewLocalDPA(datadir) + if err != nil { + return + } + api := api.NewApi(dpa, nil) + dpa.Start() + f(api) + dpa.Stop() +} + +func createTestFilesAndUploadToSwarm(t *testing.T, api *api.Api, files map[string]fileInfo, uploadDir string) string { + + os.RemoveAll(uploadDir) + + for fname, finfo := range files { + actualPath := filepath.Join(uploadDir, fname) + filePath := filepath.Dir(actualPath) + + err := os.MkdirAll(filePath, 0777) + if err != nil { + t.Fatalf("Error creating directory '%v' : %v", filePath, err) + } + + fd, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(finfo.perm)) + if err1 != nil { + t.Fatalf("Error creating file %v: %v", actualPath, err1) + } + + fd.Write(finfo.contents) + fd.Chown(finfo.uid, finfo.gid) + fd.Chmod(os.FileMode(finfo.perm)) + fd.Sync() + fd.Close() + } + + bzzhash, err := api.Upload(uploadDir, "") + if err != nil { + t.Fatalf("Error uploading directory %v: %v", uploadDir, err) + } + + return bzzhash +} + +func mountDir(t *testing.T, api *api.Api, files map[string]fileInfo, bzzHash string, mountDir string) *SwarmFS { + + // Test Mount + os.RemoveAll(mountDir) + os.MkdirAll(mountDir, 0777) + swarmfs := NewSwarmFS(api) + _, err := swarmfs.Mount(bzzHash, mountDir) + if isFUSEUnsupportedError(err) { + t.Skip("FUSE not supported:", err) + } else if err != nil { + t.Fatalf("Error mounting hash %v: %v", bzzHash, err) + } + + found := false + mi := swarmfs.Listmounts() + for _, minfo := range mi { + if minfo.MountPoint == mountDir { + if minfo.StartManifest != bzzHash || + minfo.LatestManifest != bzzHash || + minfo.fuseConnection == nil { + t.Fatalf("Error mounting: exp(%s): act(%s)", bzzHash, minfo.StartManifest) + } + found = true + } + } + + // Test listMounts + if found == false { + t.Fatalf("Error getting mounts information for %v: %v", mountDir, err) + } + + // Check if file and their attributes are as expected + compareGeneratedFileWithFileInMount(t, files, mountDir) + + return swarmfs + +} + +func compareGeneratedFileWithFileInMount(t *testing.T, files map[string]fileInfo, mountDir string) { + + err := filepath.Walk(mountDir, func(path string, f os.FileInfo, err error) error { + if f.IsDir() { + return nil + } + fname := path[len(mountDir)+1:] + if _, ok := files[fname]; !ok { + t.Fatalf(" file %v present in mount dir and is not expected", fname) + } + return nil + }) + if err != nil { + t.Fatalf("Error walking dir %v", mountDir) + } + + for fname, finfo := range files { + + destinationFile := filepath.Join(mountDir, fname) + + dfinfo, err := os.Stat(destinationFile) + if err != nil { + t.Fatalf("Destination file %v missing in mount: %v", fname, err) + } + + if int64(len(finfo.contents)) != dfinfo.Size() { + t.Fatalf("file %v Size mismatch source (%v) vs destination(%v)", fname, int64(len(finfo.contents)), dfinfo.Size()) + } + + if dfinfo.Mode().Perm().String() != "-rwx------" { + t.Fatalf("file %v Permission mismatch source (-rwx------) vs destination(%v)", fname, dfinfo.Mode().Perm()) + } + + fileContents, err := ioutil.ReadFile(filepath.Join(mountDir, fname)) + if err != nil { + t.Fatalf("Could not readfile %v : %v", fname, err) + } + + if bytes.Compare(fileContents, finfo.contents) != 0 { + t.Fatalf("File %v contents mismatch: %v , %v", fname, fileContents, finfo.contents) + + } + + // TODO: check uid and gid + } +} + +func checkFile(t *testing.T, testMountDir, fname string, contents []byte) { + + destinationFile := filepath.Join(testMountDir, fname) + dfinfo, err1 := os.Stat(destinationFile) + if err1 != nil { + t.Fatalf("Could not stat file %v", destinationFile) + } + if dfinfo.Size() != int64(len(contents)) { + t.Fatalf("Mismatch in size actual(%v) vs expected(%v)", dfinfo.Size(), int64(len(contents))) + } + + fd, err2 := os.OpenFile(destinationFile, os.O_RDONLY, os.FileMode(0665)) + if err2 != nil { + t.Fatalf("Could not open file %v", destinationFile) + } + newcontent := make([]byte, len(contents)) + fd.Read(newcontent) + fd.Close() + + if !bytes.Equal(contents, newcontent) { + t.Fatalf("File content mismatch expected (%v): received (%v) ", contents, newcontent) + } +} + +func getRandomBtes(size int) []byte { + contents := make([]byte, size) + rand.Read(contents) + return contents + +} + +func IsDirEmpty(name string) bool { + f, err := os.Open(name) + if err != nil { + return false + } + defer f.Close() + + _, err = f.Readdirnames(1) + if err == io.EOF { + return true + } + return false +} + +func testMountListAndUnmount(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "fuse-source") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "fuse-dest") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["2.txt"] = fileInfo{0711, 333, 444, getRandomBtes(10)} + files["3.txt"] = fileInfo{0622, 333, 444, getRandomBtes(100)} + files["4.txt"] = fileInfo{0533, 333, 444, getRandomBtes(1024)} + files["5.txt"] = fileInfo{0544, 333, 444, getRandomBtes(10)} + files["6.txt"] = fileInfo{0555, 333, 444, getRandomBtes(10)} + files["7.txt"] = fileInfo{0666, 333, 444, getRandomBtes(10)} + files["8.txt"] = fileInfo{0777, 333, 333, getRandomBtes(10)} + files["11.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10)} + files["111.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10)} + files["two/2.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10)} + files["two/2/2.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10)} + files["two/2./2.txt"] = fileInfo{0777, 444, 444, getRandomBtes(10)} + files["twice/2.txt"] = fileInfo{0777, 444, 333, getRandomBtes(200)} + files["one/two/three/four/five/six/seven/eight/nine/10.txt"] = fileInfo{0777, 333, 444, getRandomBtes(10240)} + files["one/two/three/four/five/six/six"] = fileInfo{0777, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs.Stop() + + // Check unmount + _, err := swarmfs.Unmount(testMountDir) + if err != nil { + t.Fatalf("could not unmount %v", bzzHash) + } + if !IsDirEmpty(testMountDir) { + t.Fatalf("unmount didnt work for %v", testMountDir) + } + +} + +func testMaxMounts(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + uploadDir1, _ := ioutil.TempDir(os.TempDir(), "max-upload1") + bzzHash1 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir1) + mount1, _ := ioutil.TempDir(os.TempDir(), "max-mount1") + swarmfs1 := mountDir(t, api, files, bzzHash1, mount1) + defer swarmfs1.Stop() + + files["2.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + uploadDir2, _ := ioutil.TempDir(os.TempDir(), "max-upload2") + bzzHash2 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir2) + mount2, _ := ioutil.TempDir(os.TempDir(), "max-mount2") + swarmfs2 := mountDir(t, api, files, bzzHash2, mount2) + defer swarmfs2.Stop() + + files["3.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + uploadDir3, _ := ioutil.TempDir(os.TempDir(), "max-upload3") + bzzHash3 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir3) + mount3, _ := ioutil.TempDir(os.TempDir(), "max-mount3") + swarmfs3 := mountDir(t, api, files, bzzHash3, mount3) + defer swarmfs3.Stop() + + files["4.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + uploadDir4, _ := ioutil.TempDir(os.TempDir(), "max-upload4") + bzzHash4 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir4) + mount4, _ := ioutil.TempDir(os.TempDir(), "max-mount4") + swarmfs4 := mountDir(t, api, files, bzzHash4, mount4) + defer swarmfs4.Stop() + + files["5.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + uploadDir5, _ := ioutil.TempDir(os.TempDir(), "max-upload5") + bzzHash5 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir5) + mount5, _ := ioutil.TempDir(os.TempDir(), "max-mount5") + swarmfs5 := mountDir(t, api, files, bzzHash5, mount5) + defer swarmfs5.Stop() + + files["6.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + uploadDir6, _ := ioutil.TempDir(os.TempDir(), "max-upload6") + bzzHash6 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir6) + mount6, _ := ioutil.TempDir(os.TempDir(), "max-mount6") + + os.RemoveAll(mount6) + os.MkdirAll(mount6, 0777) + _, err := swarmfs.Mount(bzzHash6, mount6) + if err == nil { + t.Fatalf("Error: Going beyond max mounts %v", bzzHash6) + } + +} + +func testReMounts(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + uploadDir1, _ := ioutil.TempDir(os.TempDir(), "re-upload1") + bzzHash1 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir1) + testMountDir1, _ := ioutil.TempDir(os.TempDir(), "re-mount1") + swarmfs := mountDir(t, api, files, bzzHash1, testMountDir1) + defer swarmfs.Stop() + + uploadDir2, _ := ioutil.TempDir(os.TempDir(), "re-upload2") + bzzHash2 := createTestFilesAndUploadToSwarm(t, api, files, uploadDir2) + testMountDir2, _ := ioutil.TempDir(os.TempDir(), "re-mount2") + + // try mounting the same hash second time + os.RemoveAll(testMountDir2) + os.MkdirAll(testMountDir2, 0777) + _, err := swarmfs.Mount(bzzHash1, testMountDir2) + if err != nil { + t.Fatalf("Error mounting hash %v", bzzHash1) + } + + // mount a different hash in already mounted point + _, err = swarmfs.Mount(bzzHash2, testMountDir1) + if err == nil { + t.Fatalf("Error mounting hash %v", bzzHash2) + } + + // mount nonexistent hash + _, err = swarmfs.Mount("0xfea11223344", testMountDir1) + if err == nil { + t.Fatalf("Error mounting hash %v", bzzHash2) + } + +} + +func testUnmount(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + uploadDir, _ := ioutil.TempDir(os.TempDir(), "ex-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "ex-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, uploadDir) + + swarmfs := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs.Stop() + + swarmfs.Unmount(testMountDir) + + mi := swarmfs.Listmounts() + for _, minfo := range mi { + if minfo.MountPoint == testMountDir { + t.Fatalf("mount state not cleaned up in unmount case %v", testMountDir) + } + } + +} + +func testUnmountWhenResourceBusy(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "ex-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "ex-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs.Stop() + + actualPath := filepath.Join(testMountDir, "2.txt") + d, err := os.OpenFile(actualPath, os.O_RDWR, os.FileMode(0700)) + d.Write(getRandomBtes(10)) + + _, err = swarmfs.Unmount(testMountDir) + if err != nil { + t.Fatalf("could not unmount %v", bzzHash) + } + d.Close() + + mi := swarmfs.Listmounts() + for _, minfo := range mi { + if minfo.MountPoint == testMountDir { + t.Fatalf("mount state not cleaned up in unmount case %v", testMountDir) + } + } + +} +func testSeekInMultiChunkFile(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "seek-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "seek-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10240)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs.Stop() + + // Create a new file seek the second chunk + actualPath := filepath.Join(testMountDir, "1.txt") + d, _ := os.OpenFile(actualPath, os.O_RDONLY, os.FileMode(0700)) + + d.Seek(5000, 0) + + contents := make([]byte, 1024) + d.Read(contents) + finfo := files["1.txt"] + + if bytes.Compare(finfo.contents[:6024][5000:], contents) != 0 { + t.Fatalf("File seek contents mismatch") + } + d.Close() + +} + +func testCreateNewFile(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "create-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "create-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + // Create a new file in the root dir and check + actualPath := filepath.Join(testMountDir, "2.txt") + d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665)) + if err1 != nil { + t.Fatalf("Could not create file %s : %v", actualPath, err1) + } + contents := make([]byte, 11) + rand.Read(contents) + d.Write(contents) + d.Close() + + mi, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v", err2) + } + + // mount again and see if things are okay + files["2.txt"] = fileInfo{0700, 333, 444, contents} + swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir) + defer swarmfs2.Stop() + + checkFile(t, testMountDir, "2.txt", contents) + +} + +func testCreateNewFileInsideDirectory(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "createinsidedir-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "createinsidedir-mount") + + files["one/1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + // Create a new file inside a existing dir and check + dirToCreate := filepath.Join(testMountDir, "one") + actualPath := filepath.Join(dirToCreate, "2.txt") + d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665)) + if err1 != nil { + t.Fatalf("Could not create file %s : %v", actualPath, err1) + } + contents := make([]byte, 11) + rand.Read(contents) + d.Write(contents) + d.Close() + + mi, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v", err2) + } + + // mount again and see if things are okay + files["one/2.txt"] = fileInfo{0700, 333, 444, contents} + swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir) + defer swarmfs2.Stop() + + checkFile(t, testMountDir, "one/2.txt", contents) + +} + +func testCreateNewFileInsideNewDirectory(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "createinsidenewdir-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "createinsidenewdir-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + // Create a new file inside a existing dir and check + dirToCreate := filepath.Join(testMountDir, "one") + os.MkdirAll(dirToCreate, 0777) + actualPath := filepath.Join(dirToCreate, "2.txt") + d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665)) + if err1 != nil { + t.Fatalf("Could not create file %s : %v", actualPath, err1) + } + contents := make([]byte, 11) + rand.Read(contents) + d.Write(contents) + d.Close() + + mi, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v", err2) + } + + // mount again and see if things are okay + files["one/2.txt"] = fileInfo{0700, 333, 444, contents} + swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir) + defer swarmfs2.Stop() + + checkFile(t, testMountDir, "one/2.txt", contents) + +} + +func testRemoveExistingFile(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "remove-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "remove-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + // Remove a file in the root dir and check + actualPath := filepath.Join(testMountDir, "five.txt") + os.Remove(actualPath) + + mi, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v", err2) + } + + // mount again and see if things are okay + delete(files, "five.txt") + swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir) + defer swarmfs2.Stop() + +} + +func testRemoveExistingFileInsideADir(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "remove-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "remove-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["one/five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["one/six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + // Remove a file in the root dir and check + actualPath := filepath.Join(testMountDir, "one/five.txt") + os.Remove(actualPath) + + mi, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v", err2) + } + + // mount again and see if things are okay + delete(files, "one/five.txt") + swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir) + defer swarmfs2.Stop() + +} + +func testRemoveNewlyAddedFile(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "removenew-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "removenew-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + // Adda a new file and remove it + dirToCreate := filepath.Join(testMountDir, "one") + os.MkdirAll(dirToCreate, os.FileMode(0665)) + actualPath := filepath.Join(dirToCreate, "2.txt") + d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665)) + if err1 != nil { + t.Fatalf("Could not create file %s : %v", actualPath, err1) + } + contents := make([]byte, 11) + rand.Read(contents) + d.Write(contents) + d.Close() + + checkFile(t, testMountDir, "one/2.txt", contents) + + os.Remove(actualPath) + + mi, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v", err2) + } + + // mount again and see if things are okay + swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir) + defer swarmfs2.Stop() + + if bzzHash != mi.LatestManifest { + t.Fatalf("same contents different hash orig(%v): new(%v)", bzzHash, mi.LatestManifest) + } + +} + +func testAddNewFileAndModifyContents(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "modifyfile-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "modifyfile-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + // Create a new file in the root dir and check + actualPath := filepath.Join(testMountDir, "2.txt") + d, err1 := os.OpenFile(actualPath, os.O_RDWR|os.O_CREATE, os.FileMode(0665)) + if err1 != nil { + t.Fatalf("Could not create file %s : %v", actualPath, err1) + } + line1 := []byte("Line 1") + rand.Read(line1) + d.Write(line1) + d.Close() + + mi1, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v", err2) + } + + // mount again and see if things are okay + files["2.txt"] = fileInfo{0700, 333, 444, line1} + swarmfs2 := mountDir(t, api, files, mi1.LatestManifest, testMountDir) + defer swarmfs2.Stop() + + checkFile(t, testMountDir, "2.txt", line1) + + mi2, err3 := swarmfs2.Unmount(testMountDir) + if err3 != nil { + t.Fatalf("Could not unmount %v", err3) + } + + // mount again and modify + swarmfs3 := mountDir(t, api, files, mi2.LatestManifest, testMountDir) + defer swarmfs3.Stop() + + fd, err4 := os.OpenFile(actualPath, os.O_RDWR|os.O_APPEND, os.FileMode(0665)) + if err4 != nil { + t.Fatalf("Could not create file %s : %v", actualPath, err4) + } + line2 := []byte("Line 2") + rand.Read(line2) + fd.Seek(int64(len(line1)), 0) + fd.Write(line2) + fd.Close() + + mi3, err5 := swarmfs3.Unmount(testMountDir) + if err5 != nil { + t.Fatalf("Could not unmount %v", err5) + } + + // mount again and see if things are okay + b := [][]byte{line1, line2} + line1and2 := bytes.Join(b, []byte("")) + files["2.txt"] = fileInfo{0700, 333, 444, line1and2} + swarmfs4 := mountDir(t, api, files, mi3.LatestManifest, testMountDir) + defer swarmfs4.Stop() + + checkFile(t, testMountDir, "2.txt", line1and2) + +} + +func testRemoveEmptyDir(api *api.Api, t *testing.T) { + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "rmdir-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "rmdir-mount") + + files["1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + os.MkdirAll(filepath.Join(testMountDir, "newdir"), 0777) + + mi, err3 := swarmfs1.Unmount(testMountDir) + if err3 != nil { + t.Fatalf("Could not unmount %v", err3) + } + + if bzzHash != mi.LatestManifest { + t.Fatalf("same contents different hash orig(%v): new(%v)", bzzHash, mi.LatestManifest) + } + +} + +func testRemoveDirWhichHasFiles(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "rmdir-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "rmdir-mount") + + files["one/1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["two/five.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["two/six.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + dirPath := filepath.Join(testMountDir, "two") + os.RemoveAll(dirPath) + + mi, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v ", err2) + } + + // mount again and see if things are okay + delete(files, "two/five.txt") + delete(files, "two/six.txt") + + swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir) + defer swarmfs2.Stop() + +} + +func testRemoveDirWhichHasSubDirs(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "rmsubdir-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "rmsubdir-mount") + + files["one/1.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["two/three/2.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["two/three/3.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["two/four/5.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["two/four/6.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + files["two/four/six/7.txt"] = fileInfo{0700, 333, 444, getRandomBtes(10)} + + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + dirPath := filepath.Join(testMountDir, "two") + os.RemoveAll(dirPath) + + mi, err2 := swarmfs1.Unmount(testMountDir) + if err2 != nil { + t.Fatalf("Could not unmount %v ", err2) + } + + // mount again and see if things are okay + delete(files, "two/three/2.txt") + delete(files, "two/three/3.txt") + delete(files, "two/four/5.txt") + delete(files, "two/four/6.txt") + delete(files, "two/four/six/7.txt") + + swarmfs2 := mountDir(t, api, files, mi.LatestManifest, testMountDir) + defer swarmfs2.Stop() + +} + +func testAppendFileContentsToEnd(api *api.Api, t *testing.T) { + + files := make(map[string]fileInfo) + testUploadDir, _ := ioutil.TempDir(os.TempDir(), "appendlargefile-upload") + testMountDir, _ := ioutil.TempDir(os.TempDir(), "appendlargefile-mount") + + line1 := make([]byte, 10) + rand.Read(line1) + files["1.txt"] = fileInfo{0700, 333, 444, line1} + bzzHash := createTestFilesAndUploadToSwarm(t, api, files, testUploadDir) + + swarmfs1 := mountDir(t, api, files, bzzHash, testMountDir) + defer swarmfs1.Stop() + + actualPath := filepath.Join(testMountDir, "1.txt") + fd, err4 := os.OpenFile(actualPath, os.O_RDWR|os.O_APPEND, os.FileMode(0665)) + if err4 != nil { + t.Fatalf("Could not create file %s : %v", actualPath, err4) + } + line2 := make([]byte, 5) + rand.Read(line2) + fd.Seek(int64(len(line1)), 0) + fd.Write(line2) + fd.Close() + + mi1, err5 := swarmfs1.Unmount(testMountDir) + if err5 != nil { + t.Fatalf("Could not unmount %v ", err5) + } + + // mount again and see if things are okay + b := [][]byte{line1, line2} + line1and2 := bytes.Join(b, []byte("")) + files["1.txt"] = fileInfo{0700, 333, 444, line1and2} + swarmfs2 := mountDir(t, api, files, mi1.LatestManifest, testMountDir) + defer swarmfs2.Stop() + + checkFile(t, testMountDir, "1.txt", line1and2) + +} + +func TestSwarmFileSystem(t *testing.T) { + testFuseFileSystem(t, func(api *api.Api) { + + testMountListAndUnmount(api, t) + + testMaxMounts(api, t) + + testReMounts(api, t) + + testUnmount(api, t) + + testUnmountWhenResourceBusy(api, t) + + testSeekInMultiChunkFile(api, t) + + testCreateNewFile(api, t) + + testCreateNewFileInsideDirectory(api, t) + + testCreateNewFileInsideNewDirectory(api, t) + + testRemoveExistingFile(api, t) + + testRemoveExistingFileInsideADir(api, t) + + testRemoveNewlyAddedFile(api, t) + + testAddNewFileAndModifyContents(api, t) + + testRemoveEmptyDir(api, t) + + testRemoveDirWhichHasFiles(api, t) + + testRemoveDirWhichHasSubDirs(api, t) + + testAppendFileContentsToEnd(api, t) + + }) +} diff --git a/swarm/fuse/swarmfs_unix.go b/swarm/fuse/swarmfs_unix.go new file mode 100644 index 000000000..f4eecef24 --- /dev/null +++ b/swarm/fuse/swarmfs_unix.go @@ -0,0 +1,240 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// +build linux darwin freebsd + +package fuse + +import ( + "bazil.org/fuse" + "bazil.org/fuse/fs" + "errors" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/swarm/api" + "os" + "path/filepath" + "strings" + "sync" + "time" +) + +var ( + errEmptyMountPoint = errors.New("need non-empty mount point") + errMaxMountCount = errors.New("max FUSE mount count reached") + errMountTimeout = errors.New("mount timeout") + errAlreadyMounted = errors.New("mount point is already serving") +) + +func isFUSEUnsupportedError(err error) bool { + if perr, ok := err.(*os.PathError); ok { + return perr.Op == "open" && perr.Path == "/dev/fuse" + } + return err == fuse.ErrOSXFUSENotFound +} + +// information about every active mount +type MountInfo struct { + MountPoint string + StartManifest string + LatestManifest string + rootDir *SwarmDir + fuseConnection *fuse.Conn + swarmApi *api.Api + lock *sync.RWMutex +} + +// Inode numbers need to be unique, they are used for caching inside fuse +func newInode() uint64 { + inodeLock.Lock() + defer inodeLock.Unlock() + inode += 1 + return inode +} + +func NewMountInfo(mhash, mpoint string, sapi *api.Api) *MountInfo { + newMountInfo := &MountInfo{ + MountPoint: mpoint, + StartManifest: mhash, + LatestManifest: mhash, + rootDir: nil, + fuseConnection: nil, + swarmApi: sapi, + lock: &sync.RWMutex{}, + } + return newMountInfo +} + +func (self *SwarmFS) Mount(mhash, mountpoint string) (*MountInfo, error) { + + if mountpoint == "" { + return nil, errEmptyMountPoint + } + cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint)) + if err != nil { + return nil, err + } + + self.swarmFsLock.Lock() + defer self.swarmFsLock.Unlock() + + noOfActiveMounts := len(self.activeMounts) + if noOfActiveMounts >= maxFuseMounts { + return nil, errMaxMountCount + } + + if _, ok := self.activeMounts[cleanedMountPoint]; ok { + return nil, errAlreadyMounted + } + + log.Info(fmt.Sprintf("Attempting to mount %s ", cleanedMountPoint)) + key, manifestEntryMap, err := self.swarmApi.BuildDirectoryTree(mhash, true) + if err != nil { + return nil, err + } + + mi := NewMountInfo(mhash, cleanedMountPoint, self.swarmApi) + + dirTree := map[string]*SwarmDir{} + rootDir := NewSwarmDir("/", mi) + dirTree["/"] = rootDir + mi.rootDir = rootDir + + for suffix, entry := range manifestEntryMap { + + key = common.Hex2Bytes(entry.Hash) + fullpath := "/" + suffix + basepath := filepath.Dir(fullpath) + + parentDir := rootDir + dirUntilNow := "" + paths := strings.Split(basepath, "/") + for i := range paths { + if paths[i] != "" { + thisDir := paths[i] + dirUntilNow = dirUntilNow + "/" + thisDir + + if _, ok := dirTree[dirUntilNow]; !ok { + dirTree[dirUntilNow] = NewSwarmDir(dirUntilNow, mi) + parentDir.directories = append(parentDir.directories, dirTree[dirUntilNow]) + parentDir = dirTree[dirUntilNow] + + } else { + parentDir = dirTree[dirUntilNow] + } + + } + } + thisFile := NewSwarmFile(basepath, filepath.Base(fullpath), mi) + thisFile.key = key + + parentDir.files = append(parentDir.files, thisFile) + } + + fconn, err := fuse.Mount(cleanedMountPoint, fuse.FSName("swarmfs"), fuse.VolumeName(mhash)) + if isFUSEUnsupportedError(err) { + log.Warn("Fuse not installed", "mountpoint", cleanedMountPoint, "err", err) + return nil, err + } else if err != nil { + fuse.Unmount(cleanedMountPoint) + log.Warn("Error mounting swarm manifest", "mountpoint", cleanedMountPoint, "err", err) + return nil, err + } + mi.fuseConnection = fconn + + serverr := make(chan error, 1) + go func() { + log.Info(fmt.Sprintf("Serving %s at %s", mhash, cleanedMountPoint)) + filesys := &SwarmRoot{root: rootDir} + if err := fs.Serve(fconn, filesys); err != nil { + log.Warn(fmt.Sprintf("Could not Serve SwarmFileSystem error: %v", err)) + serverr <- err + } + + }() + + // Check if the mount process has an error to report. + select { + case <-time.After(mountTimeout): + fuse.Unmount(cleanedMountPoint) + return nil, errMountTimeout + + case err := <-serverr: + fuse.Unmount(cleanedMountPoint) + log.Warn("Error serving swarm FUSE FS", "mountpoint", cleanedMountPoint, "err", err) + return nil, err + + case <-fconn.Ready: + log.Info("Now serving swarm FUSE FS", "manifest", mhash, "mountpoint", cleanedMountPoint) + } + + self.activeMounts[cleanedMountPoint] = mi + return mi, nil +} + +func (self *SwarmFS) Unmount(mountpoint string) (*MountInfo, error) { + + self.swarmFsLock.Lock() + defer self.swarmFsLock.Unlock() + + cleanedMountPoint, err := filepath.Abs(filepath.Clean(mountpoint)) + if err != nil { + return nil, err + } + + mountInfo := self.activeMounts[cleanedMountPoint] + + if mountInfo == nil || mountInfo.MountPoint != cleanedMountPoint { + return nil, fmt.Errorf("%s is not mounted", cleanedMountPoint) + } + err = fuse.Unmount(cleanedMountPoint) + if err != nil { + err1 := externalUnMount(cleanedMountPoint) + if err1 != nil { + errStr := fmt.Sprintf("UnMount error: %v", err) + log.Warn(errStr) + return nil, err1 + } + } + + mountInfo.fuseConnection.Close() + delete(self.activeMounts, cleanedMountPoint) + + succString := fmt.Sprintf("UnMounting %v succeeded", cleanedMountPoint) + log.Info(succString) + + return mountInfo, nil +} + +func (self *SwarmFS) Listmounts() []*MountInfo { + self.swarmFsLock.RLock() + defer self.swarmFsLock.RUnlock() + + rows := make([]*MountInfo, 0, len(self.activeMounts)) + for _, mi := range self.activeMounts { + rows = append(rows, mi) + } + return rows +} + +func (self *SwarmFS) Stop() bool { + for mp := range self.activeMounts { + mountInfo := self.activeMounts[mp] + self.Unmount(mountInfo.MountPoint) + } + return true +} diff --git a/swarm/fuse/swarmfs_util.go b/swarm/fuse/swarmfs_util.go new file mode 100644 index 000000000..d20ab258e --- /dev/null +++ b/swarm/fuse/swarmfs_util.go @@ -0,0 +1,144 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// +build linux darwin freebsd + +package fuse + +import ( + "fmt" + "github.com/ethereum/go-ethereum/log" + "os/exec" + "runtime" + "time" +) + +func externalUnMount(mountPoint string) error { + + var cmd *exec.Cmd + + switch runtime.GOOS { + + case "darwin": + cmd = exec.Command("/usr/bin/diskutil", "umount", "force", mountPoint) + + case "linux": + cmd = exec.Command("fusermount", "-u", mountPoint) + + default: + return fmt.Errorf("unmount: unimplemented") + } + + errc := make(chan error, 1) + go func() { + defer close(errc) + + if err := exec.Command("umount", mountPoint).Run(); err == nil { + return + } + errc <- cmd.Run() + }() + + select { + + case <-time.After(unmountTimeout): + return fmt.Errorf("umount timeout") + + case err := <-errc: + return err + } +} + +func addFileToSwarm(sf *SwarmFile, content []byte, size int) error { + + fkey, mhash, err := sf.mountInfo.swarmApi.AddFile(sf.mountInfo.LatestManifest, sf.path, sf.name, content, true) + if err != nil { + return err + } + + sf.lock.Lock() + defer sf.lock.Unlock() + sf.key = fkey + sf.fileSize = int64(size) + + sf.mountInfo.lock.Lock() + defer sf.mountInfo.lock.Unlock() + sf.mountInfo.LatestManifest = mhash + + log.Info("Added new file:", "fname", sf.name, "New Manifest hash", mhash) + return nil + +} + +func removeFileFromSwarm(sf *SwarmFile) error { + + mkey, err := sf.mountInfo.swarmApi.RemoveFile(sf.mountInfo.LatestManifest, sf.path, sf.name, true) + if err != nil { + return err + } + + sf.mountInfo.lock.Lock() + defer sf.mountInfo.lock.Unlock() + sf.mountInfo.LatestManifest = mkey + + log.Info("Removed file:", "fname", sf.name, "New Manifest hash", mkey) + return nil +} + +func removeDirectoryFromSwarm(sd *SwarmDir) error { + + if len(sd.directories) == 0 && len(sd.files) == 0 { + return nil + } + + for _, d := range sd.directories { + err := removeDirectoryFromSwarm(d) + if err != nil { + return err + } + } + + for _, f := range sd.files { + err := removeFileFromSwarm(f) + if err != nil { + return err + } + } + + return nil + +} + +func appendToExistingFileInSwarm(sf *SwarmFile, content []byte, offset int64, length int64) error { + + fkey, mhash, err := sf.mountInfo.swarmApi.AppendFile(sf.mountInfo.LatestManifest, sf.path, sf.name, sf.fileSize, content, sf.key, offset, length, true) + if err != nil { + return err + } + + sf.lock.Lock() + defer sf.lock.Unlock() + sf.key = fkey + sf.fileSize = sf.fileSize + int64(len(content)) + + sf.mountInfo.lock.Lock() + defer sf.mountInfo.lock.Unlock() + sf.mountInfo.LatestManifest = mhash + + log.Info("Appended file:", "fname", sf.name, "New Manifest hash", mhash) + return nil + +} diff --git a/swarm/network/kademlia/kademlia.go b/swarm/network/kademlia/kademlia.go index 8d731c038..bf976a3e1 100644 --- a/swarm/network/kademlia/kademlia.go +++ b/swarm/network/kademlia/kademlia.go @@ -116,7 +116,7 @@ func (self *Kademlia) DBCount() int { // On is the entry point called when a new nodes is added // unsafe in that node is not checked to be already active node (to be called once) func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) { - log.Warn(fmt.Sprintf("%v", self)) + log.Debug(fmt.Sprintf("%v", self)) defer self.lock.Unlock() self.lock.Lock() diff --git a/swarm/services/swap/swap.go b/swarm/services/swap/swap.go index eb21a598d..093892e8d 100644 --- a/swarm/services/swap/swap.go +++ b/swarm/services/swap/swap.go @@ -17,6 +17,7 @@ package swap import ( + "context" "crypto/ecdsa" "fmt" "math/big" @@ -33,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/swarm/services/swap/swap" - "golang.org/x/net/context" ) // SwAP Swarm Accounting Protocol with diff --git a/swarm/swarm.go b/swarm/swarm.go index 44564a71d..442e68d51 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -18,6 +18,7 @@ package swarm import ( "bytes" + "context" "crypto/ecdsa" "fmt" @@ -26,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/contracts/chequebook" "github.com/ethereum/go-ethereum/contracts/ens" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" @@ -33,9 +35,9 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/swarm/api" httpapi "github.com/ethereum/go-ethereum/swarm/api/http" + "github.com/ethereum/go-ethereum/swarm/fuse" "github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/storage" - "golang.org/x/net/context" ) // the swarm stack @@ -54,6 +56,7 @@ type Swarm struct { corsString string swapEnabled bool lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped + sfs *fuse.SwarmFS // need this to cleanup all the active mounts on node exit } type SwarmAPI struct { @@ -132,9 +135,13 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api. // set up high level api transactOpts := bind.NewKeyedTransactor(self.privateKey) - self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, self.backend) - if err != nil { - return nil, err + if backend == (*ethclient.Client)(nil) { + log.Warn("No ENS, please specify non-empty --ethapi to use domain name resolution") + } else { + self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, self.backend) + if err != nil { + return nil, err + } } log.Debug(fmt.Sprintf("-> Swarm Domain Name Registrar @ address %v", config.EnsRoot.Hex())) @@ -142,6 +149,9 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api. // Manifests for Smart Hosting log.Debug(fmt.Sprintf("-> Web3 virtual server API")) + self.sfs = fuse.NewSwarmFS(self.api) + log.Debug("-> Initializing Fuse file system") + return self, nil } @@ -191,7 +201,10 @@ func (self *Swarm) Start(net *p2p.Server) error { // start swarm http proxy server if self.config.Port != "" { addr := ":" + self.config.Port - go httpapi.StartHttpServer(self.api, &httpapi.Server{Addr: addr, CorsString: self.corsString}) + go httpapi.StartHttpServer(self.api, &httpapi.ServerConfig{ + Addr: addr, + CorsString: self.corsString, + }) } log.Debug(fmt.Sprintf("Swarm http proxy started on port: %v", self.config.Port)) @@ -216,7 +229,7 @@ func (self *Swarm) Stop() error { if self.lstore != nil { self.lstore.DbStore.Close() } - + self.sfs.Stop() return self.config.Save() } @@ -237,12 +250,6 @@ func (self *Swarm) APIs() []rpc.API { { Namespace: "bzz", Version: "0.1", - Service: api.NewStorage(self.api), - Public: true, - }, - { - Namespace: "bzz", - Version: "0.1", Service: &Info{self.config, chequebook.ContractParams}, Public: true, }, @@ -250,11 +257,6 @@ func (self *Swarm) APIs() []rpc.API { { Namespace: "bzz", Version: "0.1", - Service: api.NewFileSystem(self.api), - Public: false}, - { - Namespace: "bzz", - Version: "0.1", Service: api.NewControl(self.api, self.hive), Public: false, }, @@ -264,6 +266,26 @@ func (self *Swarm) APIs() []rpc.API { Service: chequebook.NewApi(self.config.Swap.Chequebook), Public: false, }, + { + Namespace: "swarmfs", + Version: fuse.Swarmfs_Version, + Service: self.sfs, + Public: false, + }, + // storage APIs + // DEPRECATED: Use the HTTP API instead + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewStorage(self.api), + Public: true, + }, + { + Namespace: "bzz", + Version: "0.1", + Service: api.NewFileSystem(self.api), + Public: false, + }, // {Namespace, Version, api.NewAdmin(self), false}, } } diff --git a/swarm/testutil/http.go b/swarm/testutil/http.go new file mode 100644 index 000000000..bf98d16eb --- /dev/null +++ b/swarm/testutil/http.go @@ -0,0 +1,56 @@ +package testutil + +import ( + "io/ioutil" + "net/http/httptest" + "os" + "testing" + + "github.com/ethereum/go-ethereum/swarm/api" + httpapi "github.com/ethereum/go-ethereum/swarm/api/http" + "github.com/ethereum/go-ethereum/swarm/storage" +) + +func NewTestSwarmServer(t *testing.T) *TestSwarmServer { + dir, err := ioutil.TempDir("", "swarm-storage-test") + if err != nil { + t.Fatal(err) + } + storeparams := &storage.StoreParams{ + ChunkDbPath: dir, + DbCapacity: 5000000, + CacheCapacity: 5000, + Radius: 0, + } + localStore, err := storage.NewLocalStore(storage.MakeHashFunc("SHA3"), storeparams) + if err != nil { + os.RemoveAll(dir) + t.Fatal(err) + } + chunker := storage.NewTreeChunker(storage.NewChunkerParams()) + dpa := &storage.DPA{ + Chunker: chunker, + ChunkStore: localStore, + } + dpa.Start() + a := api.NewApi(dpa, nil) + srv := httptest.NewServer(httpapi.NewServer(a)) + return &TestSwarmServer{ + Server: srv, + Dpa: dpa, + dir: dir, + } +} + +type TestSwarmServer struct { + *httptest.Server + + Dpa *storage.DPA + dir string +} + +func (t *TestSwarmServer) Close() { + t.Server.Close() + t.Dpa.Stop() + os.RemoveAll(t.dir) +} diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 456a4ccb8..b9678a77b 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -27,6 +27,7 @@ import ( "strings" "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/state" "github.com/ethereum/go-ethereum/core/types" @@ -35,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" ) @@ -172,7 +172,7 @@ func runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, test *Blo core.WriteHeadBlockHash(db, test.Genesis.Hash()) evmux := new(event.TypeMux) config := ¶ms.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true, EIP150Block: gasPriceFork} - chain, err := core.NewBlockChain(db, config, pow.NewSharedEthash(), evmux, vm.Config{}) + chain, err := core.NewBlockChain(db, config, ethash.NewShared(), evmux, vm.Config{}) if err != nil { return err } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 81f49efa5..c1892cdcc 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -214,7 +214,7 @@ func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx m snapshot := statedb.Snapshot() ret, gasUsed, err := core.ApplyMessage(environment, msg, gaspool) - if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { + if err != nil { statedb.RevertToSnapshot(snapshot) } statedb.Commit(chainConfig.IsEIP158(environment.Context.BlockNumber)) diff --git a/trie/iterator.go b/trie/iterator.go index 234c49ecc..42149a7d3 100644 --- a/trie/iterator.go +++ b/trie/iterator.go @@ -18,6 +18,7 @@ package trie import ( "bytes" + "container/heap" "github.com/ethereum/go-ethereum/common" ) @@ -267,6 +268,26 @@ outer: return nil } +func compareNodes(a, b NodeIterator) int { + cmp := bytes.Compare(a.Path(), b.Path()) + if cmp != 0 { + return cmp + } + + if a.Leaf() && !b.Leaf() { + return -1 + } else if b.Leaf() && !a.Leaf() { + return 1 + } + + cmp = bytes.Compare(a.Hash().Bytes(), b.Hash().Bytes()) + if cmp != 0 { + return cmp + } + + return bytes.Compare(a.LeafBlob(), b.LeafBlob()) +} + type differenceIterator struct { a, b NodeIterator // Nodes returned are those in b - a. eof bool // Indicates a has run out of elements @@ -320,8 +341,7 @@ func (it *differenceIterator) Next(bool) bool { } for { - apath, bpath := it.a.Path(), it.b.Path() - switch bytes.Compare(apath, bpath) { + switch compareNodes(it.a, it.b) { case -1: // b jumped past a; advance a if !it.a.Next(true) { @@ -333,15 +353,6 @@ func (it *differenceIterator) Next(bool) bool { // b is before a return true case 0: - if it.a.Hash() != it.b.Hash() || it.a.Leaf() != it.b.Leaf() { - // Keys are identical, but hashes or leaf status differs - return true - } - if it.a.Leaf() && it.b.Leaf() && !bytes.Equal(it.a.LeafBlob(), it.b.LeafBlob()) { - // Both are leaf nodes, but with different values - return true - } - // a and b are identical; skip this whole subtree if the nodes have hashes hasHash := it.a.Hash() == common.Hash{} if !it.b.Next(hasHash) { @@ -363,3 +374,107 @@ func (it *differenceIterator) Error() error { } return it.b.Error() } + +type nodeIteratorHeap []NodeIterator + +func (h nodeIteratorHeap) Len() int { return len(h) } +func (h nodeIteratorHeap) Less(i, j int) bool { return compareNodes(h[i], h[j]) < 0 } +func (h nodeIteratorHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *nodeIteratorHeap) Push(x interface{}) { *h = append(*h, x.(NodeIterator)) } +func (h *nodeIteratorHeap) Pop() interface{} { + n := len(*h) + x := (*h)[n-1] + *h = (*h)[0 : n-1] + return x +} + +type unionIterator struct { + items *nodeIteratorHeap // Nodes returned are the union of the ones in these iterators + count int // Number of nodes scanned across all tries + err error // The error, if one has been encountered +} + +// NewUnionIterator constructs a NodeIterator that iterates over elements in the union +// of the provided NodeIterators. Returns the iterator, and a pointer to an integer +// recording the number of nodes visited. +func NewUnionIterator(iters []NodeIterator) (NodeIterator, *int) { + h := make(nodeIteratorHeap, len(iters)) + copy(h, iters) + heap.Init(&h) + + ui := &unionIterator{ + items: &h, + } + return ui, &ui.count +} + +func (it *unionIterator) Hash() common.Hash { + return (*it.items)[0].Hash() +} + +func (it *unionIterator) Parent() common.Hash { + return (*it.items)[0].Parent() +} + +func (it *unionIterator) Leaf() bool { + return (*it.items)[0].Leaf() +} + +func (it *unionIterator) LeafBlob() []byte { + return (*it.items)[0].LeafBlob() +} + +func (it *unionIterator) Path() []byte { + return (*it.items)[0].Path() +} + +// Next returns the next node in the union of tries being iterated over. +// +// It does this by maintaining a heap of iterators, sorted by the iteration +// order of their next elements, with one entry for each source trie. Each +// time Next() is called, it takes the least element from the heap to return, +// advancing any other iterators that also point to that same element. These +// iterators are called with descend=false, since we know that any nodes under +// these nodes will also be duplicates, found in the currently selected iterator. +// Whenever an iterator is advanced, it is pushed back into the heap if it still +// has elements remaining. +// +// In the case that descend=false - eg, we're asked to ignore all subnodes of the +// current node - we also advance any iterators in the heap that have the current +// path as a prefix. +func (it *unionIterator) Next(descend bool) bool { + if len(*it.items) == 0 { + return false + } + + // Get the next key from the union + least := heap.Pop(it.items).(NodeIterator) + + // Skip over other nodes as long as they're identical, or, if we're not descending, as + // long as they have the same prefix as the current node. + for len(*it.items) > 0 && ((!descend && bytes.HasPrefix((*it.items)[0].Path(), least.Path())) || compareNodes(least, (*it.items)[0]) == 0) { + skipped := heap.Pop(it.items).(NodeIterator) + // Skip the whole subtree if the nodes have hashes; otherwise just skip this node + if skipped.Next(skipped.Hash() == common.Hash{}) { + it.count += 1 + // If there are more elements, push the iterator back on the heap + heap.Push(it.items, skipped) + } + } + + if least.Next(descend) { + it.count += 1 + heap.Push(it.items, least) + } + + return len(*it.items) > 0 +} + +func (it *unionIterator) Error() error { + for i := 0; i < len(*it.items); i++ { + if err := (*it.items)[i].Error(); err != nil { + return err + } + } + return nil +} diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 0ad9711ed..c101bb7b0 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -117,36 +117,38 @@ func TestNodeIteratorCoverage(t *testing.T) { } } +var testdata1 = []struct{ k, v string }{ + {"bar", "b"}, + {"barb", "ba"}, + {"bars", "bb"}, + {"bard", "bc"}, + {"fab", "z"}, + {"foo", "a"}, + {"food", "ab"}, + {"foos", "aa"}, +} + +var testdata2 = []struct{ k, v string }{ + {"aardvark", "c"}, + {"bar", "b"}, + {"barb", "bd"}, + {"bars", "be"}, + {"fab", "z"}, + {"foo", "a"}, + {"foos", "aa"}, + {"food", "ab"}, + {"jars", "d"}, +} + func TestDifferenceIterator(t *testing.T) { triea := newEmpty() - valsa := []struct{ k, v string }{ - {"bar", "b"}, - {"barb", "ba"}, - {"bars", "bb"}, - {"bard", "bc"}, - {"fab", "z"}, - {"foo", "a"}, - {"food", "ab"}, - {"foos", "aa"}, - } - for _, val := range valsa { + for _, val := range testdata1 { triea.Update([]byte(val.k), []byte(val.v)) } triea.Commit() trieb := newEmpty() - valsb := []struct{ k, v string }{ - {"aardvark", "c"}, - {"bar", "b"}, - {"barb", "bd"}, - {"bars", "be"}, - {"fab", "z"}, - {"foo", "a"}, - {"foos", "aa"}, - {"food", "ab"}, - {"jars", "d"}, - } - for _, val := range valsb { + for _, val := range testdata2 { trieb.Update([]byte(val.k), []byte(val.v)) } trieb.Commit() @@ -166,10 +168,57 @@ func TestDifferenceIterator(t *testing.T) { } for _, item := range all { if found[item.k] != item.v { - t.Errorf("iterator value mismatch for %s: got %q want %q", item.k, found[item.k], item.v) + t.Errorf("iterator value mismatch for %s: got %v want %v", item.k, found[item.k], item.v) } } if len(found) != len(all) { t.Errorf("iterator count mismatch: got %d values, want %d", len(found), len(all)) } } + +func TestUnionIterator(t *testing.T) { + triea := newEmpty() + for _, val := range testdata1 { + triea.Update([]byte(val.k), []byte(val.v)) + } + triea.Commit() + + trieb := newEmpty() + for _, val := range testdata2 { + trieb.Update([]byte(val.k), []byte(val.v)) + } + trieb.Commit() + + di, _ := NewUnionIterator([]NodeIterator{NewNodeIterator(triea), NewNodeIterator(trieb)}) + it := NewIteratorFromNodeIterator(di) + + all := []struct{ k, v string }{ + {"aardvark", "c"}, + {"barb", "bd"}, + {"barb", "ba"}, + {"bard", "bc"}, + {"bars", "bb"}, + {"bars", "be"}, + {"bar", "b"}, + {"fab", "z"}, + {"food", "ab"}, + {"foos", "aa"}, + {"foo", "a"}, + {"jars", "d"}, + } + + for i, kv := range all { + if !it.Next() { + t.Errorf("Iterator ends prematurely at element %d", i) + } + if kv.k != string(it.Key) { + t.Errorf("iterator value mismatch for element %d: got key %s want %s", i, it.Key, kv.k) + } + if kv.v != string(it.Value) { + t.Errorf("iterator value mismatch for element %d: got value %s want %s", i, it.Value, kv.v) + } + } + if it.Next() { + t.Errorf("Iterator returned extra values.") + } +} diff --git a/trie/trie_test.go b/trie/trie_test.go index 60307dba8..01ae3a4e7 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -377,7 +377,7 @@ func (randTest) Generate(r *rand.Rand, size int) reflect.Value { if len(allKeys) < 2 || r.Intn(100) < 10 { // new key key := make([]byte, r.Intn(50)) - randRead(r, key) + r.Read(key) allKeys = append(allKeys, key) return key } @@ -401,22 +401,6 @@ func (randTest) Generate(r *rand.Rand, size int) reflect.Value { return reflect.ValueOf(steps) } -// rand.Rand provides a Read method in Go 1.7 and later, but -// we can't use it yet. -func randRead(r *rand.Rand, b []byte) { - pos := 0 - val := 0 - for n := 0; n < len(b); n++ { - if pos == 0 { - val = r.Int() - pos = 7 - } - b[n] = byte(val) - val >>= 8 - pos-- - } -} - func runRandTest(rt randTest) bool { db, _ := ethdb.NewMemDatabase() tr, _ := New(common.Hash{}, db) diff --git a/vendor/bazil.org/fuse/LICENSE b/vendor/bazil.org/fuse/LICENSE new file mode 100644 index 000000000..4ac7cd838 --- /dev/null +++ b/vendor/bazil.org/fuse/LICENSE @@ -0,0 +1,93 @@ +Copyright (c) 2013-2015 Tommi Virtanen. +Copyright (c) 2009, 2011, 2012 The Go Authors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +The following included software components have additional copyright +notices and license terms that may differ from the above. + + +File fuse.go: + +// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c, +// which carries this notice: +// +// The files in this directory are subject to the following license. +// +// The author of this software is Russ Cox. +// +// Copyright (c) 2006 Russ Cox +// +// Permission to use, copy, modify, and distribute this software for any +// purpose without fee is hereby granted, provided that this entire notice +// is included in all copies of any software which is or includes a copy +// or modification of this software and in all copies of the supporting +// documentation for such software. +// +// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY +// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS +// FITNESS FOR ANY PARTICULAR PURPOSE. + + +File fuse_kernel.go: + +// Derived from FUSE's fuse_kernel.h +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ diff --git a/vendor/bazil.org/fuse/README.md b/vendor/bazil.org/fuse/README.md new file mode 100644 index 000000000..8c6d556ee --- /dev/null +++ b/vendor/bazil.org/fuse/README.md @@ -0,0 +1,23 @@ +bazil.org/fuse -- Filesystems in Go +=================================== + +`bazil.org/fuse` is a Go library for writing FUSE userspace +filesystems. + +It is a from-scratch implementation of the kernel-userspace +communication protocol, and does not use the C library from the +project called FUSE. `bazil.org/fuse` embraces Go fully for safety and +ease of programming. + +Here’s how to get going: + + go get bazil.org/fuse + +Website: http://bazil.org/fuse/ + +Github repository: https://github.com/bazil/fuse + +API docs: http://godoc.org/bazil.org/fuse + +Our thanks to Russ Cox for his fuse library, which this project is +based on. diff --git a/vendor/bazil.org/fuse/buffer.go b/vendor/bazil.org/fuse/buffer.go new file mode 100644 index 000000000..bb1d2b776 --- /dev/null +++ b/vendor/bazil.org/fuse/buffer.go @@ -0,0 +1,35 @@ +package fuse + +import "unsafe" + +// buffer provides a mechanism for constructing a message from +// multiple segments. +type buffer []byte + +// alloc allocates size bytes and returns a pointer to the new +// segment. +func (w *buffer) alloc(size uintptr) unsafe.Pointer { + s := int(size) + if len(*w)+s > cap(*w) { + old := *w + *w = make([]byte, len(*w), 2*cap(*w)+s) + copy(*w, old) + } + l := len(*w) + *w = (*w)[:l+s] + return unsafe.Pointer(&(*w)[l]) +} + +// reset clears out the contents of the buffer. +func (w *buffer) reset() { + for i := range (*w)[:cap(*w)] { + (*w)[i] = 0 + } + *w = (*w)[:0] +} + +func newBuffer(extra uintptr) buffer { + const hdrSize = unsafe.Sizeof(outHeader{}) + buf := make(buffer, hdrSize, hdrSize+extra) + return buf +} diff --git a/vendor/bazil.org/fuse/debug.go b/vendor/bazil.org/fuse/debug.go new file mode 100644 index 000000000..be9f900d5 --- /dev/null +++ b/vendor/bazil.org/fuse/debug.go @@ -0,0 +1,21 @@ +package fuse + +import ( + "runtime" +) + +func stack() string { + buf := make([]byte, 1024) + return string(buf[:runtime.Stack(buf, false)]) +} + +func nop(msg interface{}) {} + +// Debug is called to output debug messages, including protocol +// traces. The default behavior is to do nothing. +// +// The messages have human-friendly string representations and are +// safe to marshal to JSON. +// +// Implementations must not retain msg. +var Debug func(msg interface{}) = nop diff --git a/vendor/bazil.org/fuse/error_darwin.go b/vendor/bazil.org/fuse/error_darwin.go new file mode 100644 index 000000000..a3fb89ca2 --- /dev/null +++ b/vendor/bazil.org/fuse/error_darwin.go @@ -0,0 +1,17 @@ +package fuse + +import ( + "syscall" +) + +const ( + ENOATTR = Errno(syscall.ENOATTR) +) + +const ( + errNoXattr = ENOATTR +) + +func init() { + errnoNames[errNoXattr] = "ENOATTR" +} diff --git a/vendor/bazil.org/fuse/error_freebsd.go b/vendor/bazil.org/fuse/error_freebsd.go new file mode 100644 index 000000000..c6ea6d6e7 --- /dev/null +++ b/vendor/bazil.org/fuse/error_freebsd.go @@ -0,0 +1,15 @@ +package fuse + +import "syscall" + +const ( + ENOATTR = Errno(syscall.ENOATTR) +) + +const ( + errNoXattr = ENOATTR +) + +func init() { + errnoNames[errNoXattr] = "ENOATTR" +} diff --git a/vendor/bazil.org/fuse/error_linux.go b/vendor/bazil.org/fuse/error_linux.go new file mode 100644 index 000000000..6f113e71e --- /dev/null +++ b/vendor/bazil.org/fuse/error_linux.go @@ -0,0 +1,17 @@ +package fuse + +import ( + "syscall" +) + +const ( + ENODATA = Errno(syscall.ENODATA) +) + +const ( + errNoXattr = ENODATA +) + +func init() { + errnoNames[errNoXattr] = "ENODATA" +} diff --git a/vendor/bazil.org/fuse/error_std.go b/vendor/bazil.org/fuse/error_std.go new file mode 100644 index 000000000..398f43fbf --- /dev/null +++ b/vendor/bazil.org/fuse/error_std.go @@ -0,0 +1,31 @@ +package fuse + +// There is very little commonality in extended attribute errors +// across platforms. +// +// getxattr return value for "extended attribute does not exist" is +// ENOATTR on OS X, and ENODATA on Linux and apparently at least +// NetBSD. There may be a #define ENOATTR on Linux too, but the value +// is ENODATA in the actual syscalls. FreeBSD and OpenBSD have no +// ENODATA, only ENOATTR. ENOATTR is not in any of the standards, +// ENODATA exists but is only used for STREAMs. +// +// Each platform will define it a errNoXattr constant, and this file +// will enforce that it implements the right interfaces and hide the +// implementation. +// +// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html +// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013090.html +// http://mail-index.netbsd.org/tech-kern/2012/04/30/msg013097.html +// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html +// http://www.freebsd.org/cgi/man.cgi?query=extattr_get_file&sektion=2 +// http://nixdoc.net/man-pages/openbsd/man2/extattr_get_file.2.html + +// ErrNoXattr is a platform-independent error value meaning the +// extended attribute was not found. It can be used to respond to +// GetxattrRequest and such. +const ErrNoXattr = errNoXattr + +var _ error = ErrNoXattr +var _ Errno = ErrNoXattr +var _ ErrorNumber = ErrNoXattr diff --git a/vendor/bazil.org/fuse/fs/serve.go b/vendor/bazil.org/fuse/fs/serve.go new file mode 100644 index 000000000..e9fc56590 --- /dev/null +++ b/vendor/bazil.org/fuse/fs/serve.go @@ -0,0 +1,1568 @@ +// FUSE service loop, for servers that wish to use it. + +package fs // import "bazil.org/fuse/fs" + +import ( + "encoding/binary" + "fmt" + "hash/fnv" + "io" + "log" + "reflect" + "runtime" + "strings" + "sync" + "time" + + "golang.org/x/net/context" +) + +import ( + "bytes" + + "bazil.org/fuse" + "bazil.org/fuse/fuseutil" +) + +const ( + attrValidTime = 1 * time.Minute + entryValidTime = 1 * time.Minute +) + +// TODO: FINISH DOCS + +// An FS is the interface required of a file system. +// +// Other FUSE requests can be handled by implementing methods from the +// FS* interfaces, for example FSStatfser. +type FS interface { + // Root is called to obtain the Node for the file system root. + Root() (Node, error) +} + +type FSStatfser interface { + // Statfs is called to obtain file system metadata. + // It should write that data to resp. + Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error +} + +type FSDestroyer interface { + // Destroy is called when the file system is shutting down. + // + // Linux only sends this request for block device backed (fuseblk) + // filesystems, to allow them to flush writes to disk before the + // unmount completes. + Destroy() +} + +type FSInodeGenerator interface { + // GenerateInode is called to pick a dynamic inode number when it + // would otherwise be 0. + // + // Not all filesystems bother tracking inodes, but FUSE requires + // the inode to be set, and fewer duplicates in general makes UNIX + // tools work better. + // + // Operations where the nodes may return 0 inodes include Getattr, + // Setattr and ReadDir. + // + // If FS does not implement FSInodeGenerator, GenerateDynamicInode + // is used. + // + // Implementing this is useful to e.g. constrain the range of + // inode values used for dynamic inodes. + GenerateInode(parentInode uint64, name string) uint64 +} + +// A Node is the interface required of a file or directory. +// See the documentation for type FS for general information +// pertaining to all methods. +// +// A Node must be usable as a map key, that is, it cannot be a +// function, map or slice. +// +// Other FUSE requests can be handled by implementing methods from the +// Node* interfaces, for example NodeOpener. +// +// Methods returning Node should take care to return the same Node +// when the result is logically the same instance. Without this, each +// Node will get a new NodeID, causing spurious cache invalidations, +// extra lookups and aliasing anomalies. This may not matter for a +// simple, read-only filesystem. +type Node interface { + // Attr fills attr with the standard metadata for the node. + // + // Fields with reasonable defaults are prepopulated. For example, + // all times are set to a fixed moment when the program started. + // + // If Inode is left as 0, a dynamic inode number is chosen. + // + // The result may be cached for the duration set in Valid. + Attr(ctx context.Context, attr *fuse.Attr) error +} + +type NodeGetattrer interface { + // Getattr obtains the standard metadata for the receiver. + // It should store that metadata in resp. + // + // If this method is not implemented, the attributes will be + // generated based on Attr(), with zero values filled in. + Getattr(ctx context.Context, req *fuse.GetattrRequest, resp *fuse.GetattrResponse) error +} + +type NodeSetattrer interface { + // Setattr sets the standard metadata for the receiver. + // + // Note, this is also used to communicate changes in the size of + // the file, outside of Writes. + // + // req.Valid is a bitmask of what fields are actually being set. + // For example, the method should not change the mode of the file + // unless req.Valid.Mode() is true. + Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error +} + +type NodeSymlinker interface { + // Symlink creates a new symbolic link in the receiver, which must be a directory. + // + // TODO is the above true about directories? + Symlink(ctx context.Context, req *fuse.SymlinkRequest) (Node, error) +} + +// This optional request will be called only for symbolic link nodes. +type NodeReadlinker interface { + // Readlink reads a symbolic link. + Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) +} + +type NodeLinker interface { + // Link creates a new directory entry in the receiver based on an + // existing Node. Receiver must be a directory. + Link(ctx context.Context, req *fuse.LinkRequest, old Node) (Node, error) +} + +type NodeRemover interface { + // Remove removes the entry with the given name from + // the receiver, which must be a directory. The entry to be removed + // may correspond to a file (unlink) or to a directory (rmdir). + Remove(ctx context.Context, req *fuse.RemoveRequest) error +} + +type NodeAccesser interface { + // Access checks whether the calling context has permission for + // the given operations on the receiver. If so, Access should + // return nil. If not, Access should return EPERM. + // + // Note that this call affects the result of the access(2) system + // call but not the open(2) system call. If Access is not + // implemented, the Node behaves as if it always returns nil + // (permission granted), relying on checks in Open instead. + Access(ctx context.Context, req *fuse.AccessRequest) error +} + +type NodeStringLookuper interface { + // Lookup looks up a specific entry in the receiver, + // which must be a directory. Lookup should return a Node + // corresponding to the entry. If the name does not exist in + // the directory, Lookup should return ENOENT. + // + // Lookup need not to handle the names "." and "..". + Lookup(ctx context.Context, name string) (Node, error) +} + +type NodeRequestLookuper interface { + // Lookup looks up a specific entry in the receiver. + // See NodeStringLookuper for more. + Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (Node, error) +} + +type NodeMkdirer interface { + Mkdir(ctx context.Context, req *fuse.MkdirRequest) (Node, error) +} + +type NodeOpener interface { + // Open opens the receiver. After a successful open, a client + // process has a file descriptor referring to this Handle. + // + // Open can also be also called on non-files. For example, + // directories are Opened for ReadDir or fchdir(2). + // + // If this method is not implemented, the open will always + // succeed, and the Node itself will be used as the Handle. + // + // XXX note about access. XXX OpenFlags. + Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (Handle, error) +} + +type NodeCreater interface { + // Create creates a new directory entry in the receiver, which + // must be a directory. + Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (Node, Handle, error) +} + +type NodeForgetter interface { + // Forget about this node. This node will not receive further + // method calls. + // + // Forget is not necessarily seen on unmount, as all nodes are + // implicitly forgotten as part part of the unmount. + Forget() +} + +type NodeRenamer interface { + Rename(ctx context.Context, req *fuse.RenameRequest, newDir Node) error +} + +type NodeMknoder interface { + Mknod(ctx context.Context, req *fuse.MknodRequest) (Node, error) +} + +// TODO this should be on Handle not Node +type NodeFsyncer interface { + Fsync(ctx context.Context, req *fuse.FsyncRequest) error +} + +type NodeGetxattrer interface { + // Getxattr gets an extended attribute by the given name from the + // node. + // + // If there is no xattr by that name, returns fuse.ErrNoXattr. + Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error +} + +type NodeListxattrer interface { + // Listxattr lists the extended attributes recorded for the node. + Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error +} + +type NodeSetxattrer interface { + // Setxattr sets an extended attribute with the given name and + // value for the node. + Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error +} + +type NodeRemovexattrer interface { + // Removexattr removes an extended attribute for the name. + // + // If there is no xattr by that name, returns fuse.ErrNoXattr. + Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error +} + +var startTime = time.Now() + +func nodeAttr(ctx context.Context, n Node, attr *fuse.Attr) error { + attr.Valid = attrValidTime + attr.Nlink = 1 + attr.Atime = startTime + attr.Mtime = startTime + attr.Ctime = startTime + attr.Crtime = startTime + if err := n.Attr(ctx, attr); err != nil { + return err + } + return nil +} + +// A Handle is the interface required of an opened file or directory. +// See the documentation for type FS for general information +// pertaining to all methods. +// +// Other FUSE requests can be handled by implementing methods from the +// Handle* interfaces. The most common to implement are HandleReader, +// HandleReadDirer, and HandleWriter. +// +// TODO implement methods: Getlk, Setlk, Setlkw +type Handle interface { +} + +type HandleFlusher interface { + // Flush is called each time the file or directory is closed. + // Because there can be multiple file descriptors referring to a + // single opened file, Flush can be called multiple times. + Flush(ctx context.Context, req *fuse.FlushRequest) error +} + +type HandleReadAller interface { + ReadAll(ctx context.Context) ([]byte, error) +} + +type HandleReadDirAller interface { + ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) +} + +type HandleReader interface { + // Read requests to read data from the handle. + // + // There is a page cache in the kernel that normally submits only + // page-aligned reads spanning one or more pages. However, you + // should not rely on this. To see individual requests as + // submitted by the file system clients, set OpenDirectIO. + // + // Note that reads beyond the size of the file as reported by Attr + // are not even attempted (except in OpenDirectIO mode). + Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error +} + +type HandleWriter interface { + // Write requests to write data into the handle at the given offset. + // Store the amount of data written in resp.Size. + // + // There is a writeback page cache in the kernel that normally submits + // only page-aligned writes spanning one or more pages. However, + // you should not rely on this. To see individual requests as + // submitted by the file system clients, set OpenDirectIO. + // + // Writes that grow the file are expected to update the file size + // (as seen through Attr). Note that file size changes are + // communicated also through Setattr. + Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error +} + +type HandleReleaser interface { + Release(ctx context.Context, req *fuse.ReleaseRequest) error +} + +type Config struct { + // Function to send debug log messages to. If nil, use fuse.Debug. + // Note that changing this or fuse.Debug may not affect existing + // calls to Serve. + // + // See fuse.Debug for the rules that log functions must follow. + Debug func(msg interface{}) + + // Function to put things into context for processing the request. + // The returned context must have ctx as its parent. + // + // Note that changing this may not affect existing calls to Serve. + // + // Must not retain req. + WithContext func(ctx context.Context, req fuse.Request) context.Context +} + +// New returns a new FUSE server ready to serve this kernel FUSE +// connection. +// +// Config may be nil. +func New(conn *fuse.Conn, config *Config) *Server { + s := &Server{ + conn: conn, + req: map[fuse.RequestID]*serveRequest{}, + nodeRef: map[Node]fuse.NodeID{}, + dynamicInode: GenerateDynamicInode, + } + if config != nil { + s.debug = config.Debug + s.context = config.WithContext + } + if s.debug == nil { + s.debug = fuse.Debug + } + return s +} + +type Server struct { + // set in New + conn *fuse.Conn + debug func(msg interface{}) + context func(ctx context.Context, req fuse.Request) context.Context + + // set once at Serve time + fs FS + dynamicInode func(parent uint64, name string) uint64 + + // state, protected by meta + meta sync.Mutex + req map[fuse.RequestID]*serveRequest + node []*serveNode + nodeRef map[Node]fuse.NodeID + handle []*serveHandle + freeNode []fuse.NodeID + freeHandle []fuse.HandleID + nodeGen uint64 + + // Used to ensure worker goroutines finish before Serve returns + wg sync.WaitGroup +} + +// Serve serves the FUSE connection by making calls to the methods +// of fs and the Nodes and Handles it makes available. It returns only +// when the connection has been closed or an unexpected error occurs. +func (s *Server) Serve(fs FS) error { + defer s.wg.Wait() // Wait for worker goroutines to complete before return + + s.fs = fs + if dyn, ok := fs.(FSInodeGenerator); ok { + s.dynamicInode = dyn.GenerateInode + } + + root, err := fs.Root() + if err != nil { + return fmt.Errorf("cannot obtain root node: %v", err) + } + // Recognize the root node if it's ever returned from Lookup, + // passed to Invalidate, etc. + s.nodeRef[root] = 1 + s.node = append(s.node, nil, &serveNode{ + inode: 1, + generation: s.nodeGen, + node: root, + refs: 1, + }) + s.handle = append(s.handle, nil) + + for { + req, err := s.conn.ReadRequest() + if err != nil { + if err == io.EOF { + break + } + return err + } + + s.wg.Add(1) + go func() { + defer s.wg.Done() + s.serve(req) + }() + } + return nil +} + +// Serve serves a FUSE connection with the default settings. See +// Server.Serve. +func Serve(c *fuse.Conn, fs FS) error { + server := New(c, nil) + return server.Serve(fs) +} + +type nothing struct{} + +type serveRequest struct { + Request fuse.Request + cancel func() +} + +type serveNode struct { + inode uint64 + generation uint64 + node Node + refs uint64 + + // Delay freeing the NodeID until waitgroup is done. This allows + // using the NodeID for short periods of time without holding the + // Server.meta lock. + // + // Rules: + // + // - hold Server.meta while calling wg.Add, then unlock + // - do NOT try to reacquire Server.meta + wg sync.WaitGroup +} + +func (sn *serveNode) attr(ctx context.Context, attr *fuse.Attr) error { + err := nodeAttr(ctx, sn.node, attr) + if attr.Inode == 0 { + attr.Inode = sn.inode + } + return err +} + +type serveHandle struct { + handle Handle + readData []byte + nodeID fuse.NodeID +} + +// NodeRef is deprecated. It remains here to decrease code churn on +// FUSE library users. You may remove it from your program now; +// returning the same Node values are now recognized automatically, +// without needing NodeRef. +type NodeRef struct{} + +func (c *Server) saveNode(inode uint64, node Node) (id fuse.NodeID, gen uint64) { + c.meta.Lock() + defer c.meta.Unlock() + + if id, ok := c.nodeRef[node]; ok { + sn := c.node[id] + sn.refs++ + return id, sn.generation + } + + sn := &serveNode{inode: inode, node: node, refs: 1} + if n := len(c.freeNode); n > 0 { + id = c.freeNode[n-1] + c.freeNode = c.freeNode[:n-1] + c.node[id] = sn + c.nodeGen++ + } else { + id = fuse.NodeID(len(c.node)) + c.node = append(c.node, sn) + } + sn.generation = c.nodeGen + c.nodeRef[node] = id + return id, sn.generation +} + +func (c *Server) saveHandle(handle Handle, nodeID fuse.NodeID) (id fuse.HandleID) { + c.meta.Lock() + shandle := &serveHandle{handle: handle, nodeID: nodeID} + if n := len(c.freeHandle); n > 0 { + id = c.freeHandle[n-1] + c.freeHandle = c.freeHandle[:n-1] + c.handle[id] = shandle + } else { + id = fuse.HandleID(len(c.handle)) + c.handle = append(c.handle, shandle) + } + c.meta.Unlock() + return +} + +type nodeRefcountDropBug struct { + N uint64 + Refs uint64 + Node fuse.NodeID +} + +func (n *nodeRefcountDropBug) String() string { + return fmt.Sprintf("bug: trying to drop %d of %d references to %v", n.N, n.Refs, n.Node) +} + +func (c *Server) dropNode(id fuse.NodeID, n uint64) (forget bool) { + c.meta.Lock() + defer c.meta.Unlock() + snode := c.node[id] + + if snode == nil { + // this should only happen if refcounts kernel<->us disagree + // *and* two ForgetRequests for the same node race each other; + // this indicates a bug somewhere + c.debug(nodeRefcountDropBug{N: n, Node: id}) + + // we may end up triggering Forget twice, but that's better + // than not even once, and that's the best we can do + return true + } + + if n > snode.refs { + c.debug(nodeRefcountDropBug{N: n, Refs: snode.refs, Node: id}) + n = snode.refs + } + + snode.refs -= n + if snode.refs == 0 { + snode.wg.Wait() + c.node[id] = nil + delete(c.nodeRef, snode.node) + c.freeNode = append(c.freeNode, id) + return true + } + return false +} + +func (c *Server) dropHandle(id fuse.HandleID) { + c.meta.Lock() + c.handle[id] = nil + c.freeHandle = append(c.freeHandle, id) + c.meta.Unlock() +} + +type missingHandle struct { + Handle fuse.HandleID + MaxHandle fuse.HandleID +} + +func (m missingHandle) String() string { + return fmt.Sprint("missing handle: ", m.Handle, m.MaxHandle) +} + +// Returns nil for invalid handles. +func (c *Server) getHandle(id fuse.HandleID) (shandle *serveHandle) { + c.meta.Lock() + defer c.meta.Unlock() + if id < fuse.HandleID(len(c.handle)) { + shandle = c.handle[uint(id)] + } + if shandle == nil { + c.debug(missingHandle{ + Handle: id, + MaxHandle: fuse.HandleID(len(c.handle)), + }) + } + return +} + +type request struct { + Op string + Request *fuse.Header + In interface{} `json:",omitempty"` +} + +func (r request) String() string { + return fmt.Sprintf("<- %s", r.In) +} + +type logResponseHeader struct { + ID fuse.RequestID +} + +func (m logResponseHeader) String() string { + return fmt.Sprintf("ID=%v", m.ID) +} + +type response struct { + Op string + Request logResponseHeader + Out interface{} `json:",omitempty"` + // Errno contains the errno value as a string, for example "EPERM". + Errno string `json:",omitempty"` + // Error may contain a free form error message. + Error string `json:",omitempty"` +} + +func (r response) errstr() string { + s := r.Errno + if r.Error != "" { + // prefix the errno constant to the long form message + s = s + ": " + r.Error + } + return s +} + +func (r response) String() string { + switch { + case r.Errno != "" && r.Out != nil: + return fmt.Sprintf("-> [%v] %v error=%s", r.Request, r.Out, r.errstr()) + case r.Errno != "": + return fmt.Sprintf("-> [%v] %s error=%s", r.Request, r.Op, r.errstr()) + case r.Out != nil: + // make sure (seemingly) empty values are readable + switch r.Out.(type) { + case string: + return fmt.Sprintf("-> [%v] %s %q", r.Request, r.Op, r.Out) + case []byte: + return fmt.Sprintf("-> [%v] %s [% x]", r.Request, r.Op, r.Out) + default: + return fmt.Sprintf("-> [%v] %v", r.Request, r.Out) + } + default: + return fmt.Sprintf("-> [%v] %s", r.Request, r.Op) + } +} + +type notification struct { + Op string + Node fuse.NodeID + Out interface{} `json:",omitempty"` + Err string `json:",omitempty"` +} + +func (n notification) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "=> %s %v", n.Op, n.Node) + if n.Out != nil { + // make sure (seemingly) empty values are readable + switch n.Out.(type) { + case string: + fmt.Fprintf(&buf, " %q", n.Out) + case []byte: + fmt.Fprintf(&buf, " [% x]", n.Out) + default: + fmt.Fprintf(&buf, " %s", n.Out) + } + } + if n.Err != "" { + fmt.Fprintf(&buf, " Err:%v", n.Err) + } + return buf.String() +} + +type logMissingNode struct { + MaxNode fuse.NodeID +} + +func opName(req fuse.Request) string { + t := reflect.Indirect(reflect.ValueOf(req)).Type() + s := t.Name() + s = strings.TrimSuffix(s, "Request") + return s +} + +type logLinkRequestOldNodeNotFound struct { + Request *fuse.Header + In *fuse.LinkRequest +} + +func (m *logLinkRequestOldNodeNotFound) String() string { + return fmt.Sprintf("In LinkRequest (request %v), node %d not found", m.Request.Hdr().ID, m.In.OldNode) +} + +type renameNewDirNodeNotFound struct { + Request *fuse.Header + In *fuse.RenameRequest +} + +func (m *renameNewDirNodeNotFound) String() string { + return fmt.Sprintf("In RenameRequest (request %v), node %d not found", m.Request.Hdr().ID, m.In.NewDir) +} + +type handlerPanickedError struct { + Request interface{} + Err interface{} +} + +var _ error = handlerPanickedError{} + +func (h handlerPanickedError) Error() string { + return fmt.Sprintf("handler panicked: %v", h.Err) +} + +var _ fuse.ErrorNumber = handlerPanickedError{} + +func (h handlerPanickedError) Errno() fuse.Errno { + if err, ok := h.Err.(fuse.ErrorNumber); ok { + return err.Errno() + } + return fuse.DefaultErrno +} + +// handlerTerminatedError happens when a handler terminates itself +// with runtime.Goexit. This is most commonly because of incorrect use +// of testing.TB.FailNow, typically via t.Fatal. +type handlerTerminatedError struct { + Request interface{} +} + +var _ error = handlerTerminatedError{} + +func (h handlerTerminatedError) Error() string { + return fmt.Sprintf("handler terminated (called runtime.Goexit)") +} + +var _ fuse.ErrorNumber = handlerTerminatedError{} + +func (h handlerTerminatedError) Errno() fuse.Errno { + return fuse.DefaultErrno +} + +type handleNotReaderError struct { + handle Handle +} + +var _ error = handleNotReaderError{} + +func (e handleNotReaderError) Error() string { + return fmt.Sprintf("handle has no Read: %T", e.handle) +} + +var _ fuse.ErrorNumber = handleNotReaderError{} + +func (e handleNotReaderError) Errno() fuse.Errno { + return fuse.ENOTSUP +} + +func initLookupResponse(s *fuse.LookupResponse) { + s.EntryValid = entryValidTime +} + +func (c *Server) serve(r fuse.Request) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + parentCtx := ctx + if c.context != nil { + ctx = c.context(ctx, r) + } + + req := &serveRequest{Request: r, cancel: cancel} + + c.debug(request{ + Op: opName(r), + Request: r.Hdr(), + In: r, + }) + var node Node + var snode *serveNode + c.meta.Lock() + hdr := r.Hdr() + if id := hdr.Node; id != 0 { + if id < fuse.NodeID(len(c.node)) { + snode = c.node[uint(id)] + } + if snode == nil { + c.meta.Unlock() + c.debug(response{ + Op: opName(r), + Request: logResponseHeader{ID: hdr.ID}, + Error: fuse.ESTALE.ErrnoName(), + // this is the only place that sets both Error and + // Out; not sure if i want to do that; might get rid + // of len(c.node) things altogether + Out: logMissingNode{ + MaxNode: fuse.NodeID(len(c.node)), + }, + }) + r.RespondError(fuse.ESTALE) + return + } + node = snode.node + } + if c.req[hdr.ID] != nil { + // This happens with OSXFUSE. Assume it's okay and + // that we'll never see an interrupt for this one. + // Otherwise everything wedges. TODO: Report to OSXFUSE? + // + // TODO this might have been because of missing done() calls + } else { + c.req[hdr.ID] = req + } + c.meta.Unlock() + + // Call this before responding. + // After responding is too late: we might get another request + // with the same ID and be very confused. + done := func(resp interface{}) { + msg := response{ + Op: opName(r), + Request: logResponseHeader{ID: hdr.ID}, + } + if err, ok := resp.(error); ok { + msg.Error = err.Error() + if ferr, ok := err.(fuse.ErrorNumber); ok { + errno := ferr.Errno() + msg.Errno = errno.ErrnoName() + if errno == err { + // it's just a fuse.Errno with no extra detail; + // skip the textual message for log readability + msg.Error = "" + } + } else { + msg.Errno = fuse.DefaultErrno.ErrnoName() + } + } else { + msg.Out = resp + } + c.debug(msg) + + c.meta.Lock() + delete(c.req, hdr.ID) + c.meta.Unlock() + } + + var responded bool + defer func() { + if rec := recover(); rec != nil { + const size = 1 << 16 + buf := make([]byte, size) + n := runtime.Stack(buf, false) + buf = buf[:n] + log.Printf("fuse: panic in handler for %v: %v\n%s", r, rec, buf) + err := handlerPanickedError{ + Request: r, + Err: rec, + } + done(err) + r.RespondError(err) + return + } + + if !responded { + err := handlerTerminatedError{ + Request: r, + } + done(err) + r.RespondError(err) + } + }() + + if err := c.handleRequest(ctx, node, snode, r, done); err != nil { + if err == context.Canceled { + select { + case <-parentCtx.Done(): + // We canceled the parent context because of an + // incoming interrupt request, so return EINTR + // to trigger the right behavior in the client app. + // + // Only do this when it's the parent context that was + // canceled, not a context controlled by the program + // using this library, so we don't return EINTR too + // eagerly -- it might cause busy loops. + // + // Decent write-up on role of EINTR: + // http://250bpm.com/blog:12 + err = fuse.EINTR + default: + // nothing + } + } + done(err) + r.RespondError(err) + } + + // disarm runtime.Goexit protection + responded = true +} + +// handleRequest will either a) call done(s) and r.Respond(s) OR b) return an error. +func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, r fuse.Request, done func(resp interface{})) error { + switch r := r.(type) { + default: + // Note: To FUSE, ENOSYS means "this server never implements this request." + // It would be inappropriate to return ENOSYS for other operations in this + // switch that might only be unavailable in some contexts, not all. + return fuse.ENOSYS + + case *fuse.StatfsRequest: + s := &fuse.StatfsResponse{} + if fs, ok := c.fs.(FSStatfser); ok { + if err := fs.Statfs(ctx, r, s); err != nil { + return err + } + } + done(s) + r.Respond(s) + return nil + + // Node operations. + case *fuse.GetattrRequest: + s := &fuse.GetattrResponse{} + if n, ok := node.(NodeGetattrer); ok { + if err := n.Getattr(ctx, r, s); err != nil { + return err + } + } else { + if err := snode.attr(ctx, &s.Attr); err != nil { + return err + } + } + done(s) + r.Respond(s) + return nil + + case *fuse.SetattrRequest: + s := &fuse.SetattrResponse{} + if n, ok := node.(NodeSetattrer); ok { + if err := n.Setattr(ctx, r, s); err != nil { + return err + } + } + + if err := snode.attr(ctx, &s.Attr); err != nil { + return err + } + done(s) + r.Respond(s) + return nil + + case *fuse.SymlinkRequest: + s := &fuse.SymlinkResponse{} + initLookupResponse(&s.LookupResponse) + n, ok := node.(NodeSymlinker) + if !ok { + return fuse.EIO // XXX or EPERM like Mkdir? + } + n2, err := n.Symlink(ctx, r) + if err != nil { + return err + } + if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.NewName, n2); err != nil { + return err + } + done(s) + r.Respond(s) + return nil + + case *fuse.ReadlinkRequest: + n, ok := node.(NodeReadlinker) + if !ok { + return fuse.EIO /// XXX or EPERM? + } + target, err := n.Readlink(ctx, r) + if err != nil { + return err + } + done(target) + r.Respond(target) + return nil + + case *fuse.LinkRequest: + n, ok := node.(NodeLinker) + if !ok { + return fuse.EIO /// XXX or EPERM? + } + c.meta.Lock() + var oldNode *serveNode + if int(r.OldNode) < len(c.node) { + oldNode = c.node[r.OldNode] + } + c.meta.Unlock() + if oldNode == nil { + c.debug(logLinkRequestOldNodeNotFound{ + Request: r.Hdr(), + In: r, + }) + return fuse.EIO + } + n2, err := n.Link(ctx, r, oldNode.node) + if err != nil { + return err + } + s := &fuse.LookupResponse{} + initLookupResponse(s) + if err := c.saveLookup(ctx, s, snode, r.NewName, n2); err != nil { + return err + } + done(s) + r.Respond(s) + return nil + + case *fuse.RemoveRequest: + n, ok := node.(NodeRemover) + if !ok { + return fuse.EIO /// XXX or EPERM? + } + err := n.Remove(ctx, r) + if err != nil { + return err + } + done(nil) + r.Respond() + return nil + + case *fuse.AccessRequest: + if n, ok := node.(NodeAccesser); ok { + if err := n.Access(ctx, r); err != nil { + return err + } + } + done(nil) + r.Respond() + return nil + + case *fuse.LookupRequest: + var n2 Node + var err error + s := &fuse.LookupResponse{} + initLookupResponse(s) + if n, ok := node.(NodeStringLookuper); ok { + n2, err = n.Lookup(ctx, r.Name) + } else if n, ok := node.(NodeRequestLookuper); ok { + n2, err = n.Lookup(ctx, r, s) + } else { + return fuse.ENOENT + } + if err != nil { + return err + } + if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil { + return err + } + done(s) + r.Respond(s) + return nil + + case *fuse.MkdirRequest: + s := &fuse.MkdirResponse{} + initLookupResponse(&s.LookupResponse) + n, ok := node.(NodeMkdirer) + if !ok { + return fuse.EPERM + } + n2, err := n.Mkdir(ctx, r) + if err != nil { + return err + } + if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil { + return err + } + done(s) + r.Respond(s) + return nil + + case *fuse.OpenRequest: + s := &fuse.OpenResponse{} + var h2 Handle + if n, ok := node.(NodeOpener); ok { + hh, err := n.Open(ctx, r, s) + if err != nil { + return err + } + h2 = hh + } else { + h2 = node + } + s.Handle = c.saveHandle(h2, r.Hdr().Node) + done(s) + r.Respond(s) + return nil + + case *fuse.CreateRequest: + n, ok := node.(NodeCreater) + if !ok { + // If we send back ENOSYS, FUSE will try mknod+open. + return fuse.EPERM + } + s := &fuse.CreateResponse{OpenResponse: fuse.OpenResponse{}} + initLookupResponse(&s.LookupResponse) + n2, h2, err := n.Create(ctx, r, s) + if err != nil { + return err + } + if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil { + return err + } + s.Handle = c.saveHandle(h2, r.Hdr().Node) + done(s) + r.Respond(s) + return nil + + case *fuse.GetxattrRequest: + n, ok := node.(NodeGetxattrer) + if !ok { + return fuse.ENOTSUP + } + s := &fuse.GetxattrResponse{} + err := n.Getxattr(ctx, r, s) + if err != nil { + return err + } + if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) { + return fuse.ERANGE + } + done(s) + r.Respond(s) + return nil + + case *fuse.ListxattrRequest: + n, ok := node.(NodeListxattrer) + if !ok { + return fuse.ENOTSUP + } + s := &fuse.ListxattrResponse{} + err := n.Listxattr(ctx, r, s) + if err != nil { + return err + } + if r.Size != 0 && uint64(len(s.Xattr)) > uint64(r.Size) { + return fuse.ERANGE + } + done(s) + r.Respond(s) + return nil + + case *fuse.SetxattrRequest: + n, ok := node.(NodeSetxattrer) + if !ok { + return fuse.ENOTSUP + } + err := n.Setxattr(ctx, r) + if err != nil { + return err + } + done(nil) + r.Respond() + return nil + + case *fuse.RemovexattrRequest: + n, ok := node.(NodeRemovexattrer) + if !ok { + return fuse.ENOTSUP + } + err := n.Removexattr(ctx, r) + if err != nil { + return err + } + done(nil) + r.Respond() + return nil + + case *fuse.ForgetRequest: + forget := c.dropNode(r.Hdr().Node, r.N) + if forget { + n, ok := node.(NodeForgetter) + if ok { + n.Forget() + } + } + done(nil) + r.Respond() + return nil + + // Handle operations. + case *fuse.ReadRequest: + shandle := c.getHandle(r.Handle) + if shandle == nil { + return fuse.ESTALE + } + handle := shandle.handle + + s := &fuse.ReadResponse{Data: make([]byte, 0, r.Size)} + if r.Dir { + if h, ok := handle.(HandleReadDirAller); ok { + // detect rewinddir(3) or similar seek and refresh + // contents + if r.Offset == 0 { + shandle.readData = nil + } + + if shandle.readData == nil { + dirs, err := h.ReadDirAll(ctx) + if err != nil { + return err + } + var data []byte + for _, dir := range dirs { + if dir.Inode == 0 { + dir.Inode = c.dynamicInode(snode.inode, dir.Name) + } + data = fuse.AppendDirent(data, dir) + } + shandle.readData = data + } + fuseutil.HandleRead(r, s, shandle.readData) + done(s) + r.Respond(s) + return nil + } + } else { + if h, ok := handle.(HandleReadAller); ok { + if shandle.readData == nil { + data, err := h.ReadAll(ctx) + if err != nil { + return err + } + if data == nil { + data = []byte{} + } + shandle.readData = data + } + fuseutil.HandleRead(r, s, shandle.readData) + done(s) + r.Respond(s) + return nil + } + h, ok := handle.(HandleReader) + if !ok { + err := handleNotReaderError{handle: handle} + return err + } + if err := h.Read(ctx, r, s); err != nil { + return err + } + } + done(s) + r.Respond(s) + return nil + + case *fuse.WriteRequest: + shandle := c.getHandle(r.Handle) + if shandle == nil { + return fuse.ESTALE + } + + s := &fuse.WriteResponse{} + if h, ok := shandle.handle.(HandleWriter); ok { + if err := h.Write(ctx, r, s); err != nil { + return err + } + done(s) + r.Respond(s) + return nil + } + return fuse.EIO + + case *fuse.FlushRequest: + shandle := c.getHandle(r.Handle) + if shandle == nil { + return fuse.ESTALE + } + handle := shandle.handle + + if h, ok := handle.(HandleFlusher); ok { + if err := h.Flush(ctx, r); err != nil { + return err + } + } + done(nil) + r.Respond() + return nil + + case *fuse.ReleaseRequest: + shandle := c.getHandle(r.Handle) + if shandle == nil { + return fuse.ESTALE + } + handle := shandle.handle + + // No matter what, release the handle. + c.dropHandle(r.Handle) + + if h, ok := handle.(HandleReleaser); ok { + if err := h.Release(ctx, r); err != nil { + return err + } + } + done(nil) + r.Respond() + return nil + + case *fuse.DestroyRequest: + if fs, ok := c.fs.(FSDestroyer); ok { + fs.Destroy() + } + done(nil) + r.Respond() + return nil + + case *fuse.RenameRequest: + c.meta.Lock() + var newDirNode *serveNode + if int(r.NewDir) < len(c.node) { + newDirNode = c.node[r.NewDir] + } + c.meta.Unlock() + if newDirNode == nil { + c.debug(renameNewDirNodeNotFound{ + Request: r.Hdr(), + In: r, + }) + return fuse.EIO + } + n, ok := node.(NodeRenamer) + if !ok { + return fuse.EIO // XXX or EPERM like Mkdir? + } + err := n.Rename(ctx, r, newDirNode.node) + if err != nil { + return err + } + done(nil) + r.Respond() + return nil + + case *fuse.MknodRequest: + n, ok := node.(NodeMknoder) + if !ok { + return fuse.EIO + } + n2, err := n.Mknod(ctx, r) + if err != nil { + return err + } + s := &fuse.LookupResponse{} + initLookupResponse(s) + if err := c.saveLookup(ctx, s, snode, r.Name, n2); err != nil { + return err + } + done(s) + r.Respond(s) + return nil + + case *fuse.FsyncRequest: + n, ok := node.(NodeFsyncer) + if !ok { + return fuse.EIO + } + err := n.Fsync(ctx, r) + if err != nil { + return err + } + done(nil) + r.Respond() + return nil + + case *fuse.InterruptRequest: + c.meta.Lock() + ireq := c.req[r.IntrID] + if ireq != nil && ireq.cancel != nil { + ireq.cancel() + ireq.cancel = nil + } + c.meta.Unlock() + done(nil) + r.Respond() + return nil + + /* case *FsyncdirRequest: + return ENOSYS + + case *GetlkRequest, *SetlkRequest, *SetlkwRequest: + return ENOSYS + + case *BmapRequest: + return ENOSYS + + case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest: + return ENOSYS + */ + } + + panic("not reached") +} + +func (c *Server) saveLookup(ctx context.Context, s *fuse.LookupResponse, snode *serveNode, elem string, n2 Node) error { + if err := nodeAttr(ctx, n2, &s.Attr); err != nil { + return err + } + if s.Attr.Inode == 0 { + s.Attr.Inode = c.dynamicInode(snode.inode, elem) + } + + s.Node, s.Generation = c.saveNode(s.Attr.Inode, n2) + return nil +} + +type invalidateNodeDetail struct { + Off int64 + Size int64 +} + +func (i invalidateNodeDetail) String() string { + return fmt.Sprintf("Off:%d Size:%d", i.Off, i.Size) +} + +func errstr(err error) string { + if err == nil { + return "" + } + return err.Error() +} + +func (s *Server) invalidateNode(node Node, off int64, size int64) error { + s.meta.Lock() + id, ok := s.nodeRef[node] + if ok { + snode := s.node[id] + snode.wg.Add(1) + defer snode.wg.Done() + } + s.meta.Unlock() + if !ok { + // This is what the kernel would have said, if we had been + // able to send this message; it's not cached. + return fuse.ErrNotCached + } + // Delay logging until after we can record the error too. We + // consider a /dev/fuse write to be instantaneous enough to not + // need separate before and after messages. + err := s.conn.InvalidateNode(id, off, size) + s.debug(notification{ + Op: "InvalidateNode", + Node: id, + Out: invalidateNodeDetail{ + Off: off, + Size: size, + }, + Err: errstr(err), + }) + return err +} + +// InvalidateNodeAttr invalidates the kernel cache of the attributes +// of node. +// +// Returns fuse.ErrNotCached if the kernel is not currently caching +// the node. +func (s *Server) InvalidateNodeAttr(node Node) error { + return s.invalidateNode(node, 0, 0) +} + +// InvalidateNodeData invalidates the kernel cache of the attributes +// and data of node. +// +// Returns fuse.ErrNotCached if the kernel is not currently caching +// the node. +func (s *Server) InvalidateNodeData(node Node) error { + return s.invalidateNode(node, 0, -1) +} + +// InvalidateNodeDataRange invalidates the kernel cache of the +// attributes and a range of the data of node. +// +// Returns fuse.ErrNotCached if the kernel is not currently caching +// the node. +func (s *Server) InvalidateNodeDataRange(node Node, off int64, size int64) error { + return s.invalidateNode(node, off, size) +} + +type invalidateEntryDetail struct { + Name string +} + +func (i invalidateEntryDetail) String() string { + return fmt.Sprintf("%q", i.Name) +} + +// InvalidateEntry invalidates the kernel cache of the directory entry +// identified by parent node and entry basename. +// +// Kernel may or may not cache directory listings. To invalidate +// those, use InvalidateNode to invalidate all of the data for a +// directory. (As of 2015-06, Linux FUSE does not cache directory +// listings.) +// +// Returns ErrNotCached if the kernel is not currently caching the +// node. +func (s *Server) InvalidateEntry(parent Node, name string) error { + s.meta.Lock() + id, ok := s.nodeRef[parent] + if ok { + snode := s.node[id] + snode.wg.Add(1) + defer snode.wg.Done() + } + s.meta.Unlock() + if !ok { + // This is what the kernel would have said, if we had been + // able to send this message; it's not cached. + return fuse.ErrNotCached + } + err := s.conn.InvalidateEntry(id, name) + s.debug(notification{ + Op: "InvalidateEntry", + Node: id, + Out: invalidateEntryDetail{ + Name: name, + }, + Err: errstr(err), + }) + return err +} + +// DataHandle returns a read-only Handle that satisfies reads +// using the given data. +func DataHandle(data []byte) Handle { + return &dataHandle{data} +} + +type dataHandle struct { + data []byte +} + +func (d *dataHandle) ReadAll(ctx context.Context) ([]byte, error) { + return d.data, nil +} + +// GenerateDynamicInode returns a dynamic inode. +// +// The parent inode and current entry name are used as the criteria +// for choosing a pseudorandom inode. This makes it likely the same +// entry will get the same inode on multiple runs. +func GenerateDynamicInode(parent uint64, name string) uint64 { + h := fnv.New64a() + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], parent) + _, _ = h.Write(buf[:]) + _, _ = h.Write([]byte(name)) + var inode uint64 + for { + inode = h.Sum64() + if inode != 0 { + break + } + // there's a tiny probability that result is zero; change the + // input a little and try again + _, _ = h.Write([]byte{'x'}) + } + return inode +} diff --git a/vendor/bazil.org/fuse/fs/tree.go b/vendor/bazil.org/fuse/fs/tree.go new file mode 100644 index 000000000..7e078045a --- /dev/null +++ b/vendor/bazil.org/fuse/fs/tree.go @@ -0,0 +1,99 @@ +// FUSE directory tree, for servers that wish to use it with the service loop. + +package fs + +import ( + "os" + pathpkg "path" + "strings" + + "golang.org/x/net/context" +) + +import ( + "bazil.org/fuse" +) + +// A Tree implements a basic read-only directory tree for FUSE. +// The Nodes contained in it may still be writable. +type Tree struct { + tree +} + +func (t *Tree) Root() (Node, error) { + return &t.tree, nil +} + +// Add adds the path to the tree, resolving to the given node. +// If path or a prefix of path has already been added to the tree, +// Add panics. +// +// Add is only safe to call before starting to serve requests. +func (t *Tree) Add(path string, node Node) { + path = pathpkg.Clean("/" + path)[1:] + elems := strings.Split(path, "/") + dir := Node(&t.tree) + for i, elem := range elems { + dt, ok := dir.(*tree) + if !ok { + panic("fuse: Tree.Add for " + strings.Join(elems[:i], "/") + " and " + path) + } + n := dt.lookup(elem) + if n != nil { + if i+1 == len(elems) { + panic("fuse: Tree.Add for " + path + " conflicts with " + elem) + } + dir = n + } else { + if i+1 == len(elems) { + dt.add(elem, node) + } else { + dir = &tree{} + dt.add(elem, dir) + } + } + } +} + +type treeDir struct { + name string + node Node +} + +type tree struct { + dir []treeDir +} + +func (t *tree) lookup(name string) Node { + for _, d := range t.dir { + if d.name == name { + return d.node + } + } + return nil +} + +func (t *tree) add(name string, n Node) { + t.dir = append(t.dir, treeDir{name, n}) +} + +func (t *tree) Attr(ctx context.Context, a *fuse.Attr) error { + a.Mode = os.ModeDir | 0555 + return nil +} + +func (t *tree) Lookup(ctx context.Context, name string) (Node, error) { + n := t.lookup(name) + if n != nil { + return n, nil + } + return nil, fuse.ENOENT +} + +func (t *tree) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { + var out []fuse.Dirent + for _, d := range t.dir { + out = append(out, fuse.Dirent{Name: d.name}) + } + return out, nil +} diff --git a/vendor/bazil.org/fuse/fuse.go b/vendor/bazil.org/fuse/fuse.go new file mode 100644 index 000000000..6db0ef293 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse.go @@ -0,0 +1,2303 @@ +// See the file LICENSE for copyright and licensing information. + +// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c, +// which carries this notice: +// +// The files in this directory are subject to the following license. +// +// The author of this software is Russ Cox. +// +// Copyright (c) 2006 Russ Cox +// +// Permission to use, copy, modify, and distribute this software for any +// purpose without fee is hereby granted, provided that this entire notice +// is included in all copies of any software which is or includes a copy +// or modification of this software and in all copies of the supporting +// documentation for such software. +// +// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY +// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS +// FITNESS FOR ANY PARTICULAR PURPOSE. + +// Package fuse enables writing FUSE file systems on Linux, OS X, and FreeBSD. +// +// On OS X, it requires OSXFUSE (http://osxfuse.github.com/). +// +// There are two approaches to writing a FUSE file system. The first is to speak +// the low-level message protocol, reading from a Conn using ReadRequest and +// writing using the various Respond methods. This approach is closest to +// the actual interaction with the kernel and can be the simplest one in contexts +// such as protocol translators. +// +// Servers of synthesized file systems tend to share common +// bookkeeping abstracted away by the second approach, which is to +// call fs.Serve to serve the FUSE protocol using an implementation of +// the service methods in the interfaces FS* (file system), Node* (file +// or directory), and Handle* (opened file or directory). +// There are a daunting number of such methods that can be written, +// but few are required. +// The specific methods are described in the documentation for those interfaces. +// +// The hellofs subdirectory contains a simple illustration of the fs.Serve approach. +// +// Service Methods +// +// The required and optional methods for the FS, Node, and Handle interfaces +// have the general form +// +// Op(ctx context.Context, req *OpRequest, resp *OpResponse) error +// +// where Op is the name of a FUSE operation. Op reads request +// parameters from req and writes results to resp. An operation whose +// only result is the error result omits the resp parameter. +// +// Multiple goroutines may call service methods simultaneously; the +// methods being called are responsible for appropriate +// synchronization. +// +// The operation must not hold on to the request or response, +// including any []byte fields such as WriteRequest.Data or +// SetxattrRequest.Xattr. +// +// Errors +// +// Operations can return errors. The FUSE interface can only +// communicate POSIX errno error numbers to file system clients, the +// message is not visible to file system clients. The returned error +// can implement ErrorNumber to control the errno returned. Without +// ErrorNumber, a generic errno (EIO) is returned. +// +// Error messages will be visible in the debug log as part of the +// response. +// +// Interrupted Operations +// +// In some file systems, some operations +// may take an undetermined amount of time. For example, a Read waiting for +// a network message or a matching Write might wait indefinitely. If the request +// is cancelled and no longer needed, the context will be cancelled. +// Blocking operations should select on a receive from ctx.Done() and attempt to +// abort the operation early if the receive succeeds (meaning the channel is closed). +// To indicate that the operation failed because it was aborted, return fuse.EINTR. +// +// If an operation does not block for an indefinite amount of time, supporting +// cancellation is not necessary. +// +// Authentication +// +// All requests types embed a Header, meaning that the method can +// inspect req.Pid, req.Uid, and req.Gid as necessary to implement +// permission checking. The kernel FUSE layer normally prevents other +// users from accessing the FUSE file system (to change this, see +// AllowOther, AllowRoot), but does not enforce access modes (to +// change this, see DefaultPermissions). +// +// Mount Options +// +// Behavior and metadata of the mounted file system can be changed by +// passing MountOption values to Mount. +// +package fuse // import "bazil.org/fuse" + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "sync" + "syscall" + "time" + "unsafe" +) + +// A Conn represents a connection to a mounted FUSE file system. +type Conn struct { + // Ready is closed when the mount is complete or has failed. + Ready <-chan struct{} + + // MountError stores any error from the mount process. Only valid + // after Ready is closed. + MountError error + + // File handle for kernel communication. Only safe to access if + // rio or wio is held. + dev *os.File + wio sync.RWMutex + rio sync.RWMutex + + // Protocol version negotiated with InitRequest/InitResponse. + proto Protocol +} + +// MountpointDoesNotExistError is an error returned when the +// mountpoint does not exist. +type MountpointDoesNotExistError struct { + Path string +} + +var _ error = (*MountpointDoesNotExistError)(nil) + +func (e *MountpointDoesNotExistError) Error() string { + return fmt.Sprintf("mountpoint does not exist: %v", e.Path) +} + +// Mount mounts a new FUSE connection on the named directory +// and returns a connection for reading and writing FUSE messages. +// +// After a successful return, caller must call Close to free +// resources. +// +// Even on successful return, the new mount is not guaranteed to be +// visible until after Conn.Ready is closed. See Conn.MountError for +// possible errors. Incoming requests on Conn must be served to make +// progress. +func Mount(dir string, options ...MountOption) (*Conn, error) { + conf := mountConfig{ + options: make(map[string]string), + } + for _, option := range options { + if err := option(&conf); err != nil { + return nil, err + } + } + + ready := make(chan struct{}, 1) + c := &Conn{ + Ready: ready, + } + f, err := mount(dir, &conf, ready, &c.MountError) + if err != nil { + return nil, err + } + c.dev = f + + if err := initMount(c, &conf); err != nil { + c.Close() + if err == ErrClosedWithoutInit { + // see if we can provide a better error + <-c.Ready + if err := c.MountError; err != nil { + return nil, err + } + } + return nil, err + } + + return c, nil +} + +type OldVersionError struct { + Kernel Protocol + LibraryMin Protocol +} + +func (e *OldVersionError) Error() string { + return fmt.Sprintf("kernel FUSE version is too old: %v < %v", e.Kernel, e.LibraryMin) +} + +var ( + ErrClosedWithoutInit = errors.New("fuse connection closed without init") +) + +func initMount(c *Conn, conf *mountConfig) error { + req, err := c.ReadRequest() + if err != nil { + if err == io.EOF { + return ErrClosedWithoutInit + } + return err + } + r, ok := req.(*InitRequest) + if !ok { + return fmt.Errorf("missing init, got: %T", req) + } + + min := Protocol{protoVersionMinMajor, protoVersionMinMinor} + if r.Kernel.LT(min) { + req.RespondError(Errno(syscall.EPROTO)) + c.Close() + return &OldVersionError{ + Kernel: r.Kernel, + LibraryMin: min, + } + } + + proto := Protocol{protoVersionMaxMajor, protoVersionMaxMinor} + if r.Kernel.LT(proto) { + // Kernel doesn't support the latest version we have. + proto = r.Kernel + } + c.proto = proto + + s := &InitResponse{ + Library: proto, + MaxReadahead: conf.maxReadahead, + MaxWrite: maxWrite, + Flags: InitBigWrites | conf.initFlags, + } + r.Respond(s) + return nil +} + +// A Request represents a single FUSE request received from the kernel. +// Use a type switch to determine the specific kind. +// A request of unrecognized type will have concrete type *Header. +type Request interface { + // Hdr returns the Header associated with this request. + Hdr() *Header + + // RespondError responds to the request with the given error. + RespondError(error) + + String() string +} + +// A RequestID identifies an active FUSE request. +type RequestID uint64 + +func (r RequestID) String() string { + return fmt.Sprintf("%#x", uint64(r)) +} + +// A NodeID is a number identifying a directory or file. +// It must be unique among IDs returned in LookupResponses +// that have not yet been forgotten by ForgetRequests. +type NodeID uint64 + +func (n NodeID) String() string { + return fmt.Sprintf("%#x", uint64(n)) +} + +// A HandleID is a number identifying an open directory or file. +// It only needs to be unique while the directory or file is open. +type HandleID uint64 + +func (h HandleID) String() string { + return fmt.Sprintf("%#x", uint64(h)) +} + +// The RootID identifies the root directory of a FUSE file system. +const RootID NodeID = rootID + +// A Header describes the basic information sent in every request. +type Header struct { + Conn *Conn `json:"-"` // connection this request was received on + ID RequestID // unique ID for request + Node NodeID // file or directory the request is about + Uid uint32 // user ID of process making request + Gid uint32 // group ID of process making request + Pid uint32 // process ID of process making request + + // for returning to reqPool + msg *message +} + +func (h *Header) String() string { + return fmt.Sprintf("ID=%v Node=%v Uid=%d Gid=%d Pid=%d", h.ID, h.Node, h.Uid, h.Gid, h.Pid) +} + +func (h *Header) Hdr() *Header { + return h +} + +func (h *Header) noResponse() { + putMessage(h.msg) +} + +func (h *Header) respond(msg []byte) { + out := (*outHeader)(unsafe.Pointer(&msg[0])) + out.Unique = uint64(h.ID) + h.Conn.respond(msg) + putMessage(h.msg) +} + +// An ErrorNumber is an error with a specific error number. +// +// Operations may return an error value that implements ErrorNumber to +// control what specific error number (errno) to return. +type ErrorNumber interface { + // Errno returns the the error number (errno) for this error. + Errno() Errno +} + +const ( + // ENOSYS indicates that the call is not supported. + ENOSYS = Errno(syscall.ENOSYS) + + // ESTALE is used by Serve to respond to violations of the FUSE protocol. + ESTALE = Errno(syscall.ESTALE) + + ENOENT = Errno(syscall.ENOENT) + EIO = Errno(syscall.EIO) + EPERM = Errno(syscall.EPERM) + + // EINTR indicates request was interrupted by an InterruptRequest. + // See also fs.Intr. + EINTR = Errno(syscall.EINTR) + + ERANGE = Errno(syscall.ERANGE) + ENOTSUP = Errno(syscall.ENOTSUP) + EEXIST = Errno(syscall.EEXIST) +) + +// DefaultErrno is the errno used when error returned does not +// implement ErrorNumber. +const DefaultErrno = EIO + +var errnoNames = map[Errno]string{ + ENOSYS: "ENOSYS", + ESTALE: "ESTALE", + ENOENT: "ENOENT", + EIO: "EIO", + EPERM: "EPERM", + EINTR: "EINTR", + EEXIST: "EEXIST", +} + +// Errno implements Error and ErrorNumber using a syscall.Errno. +type Errno syscall.Errno + +var _ = ErrorNumber(Errno(0)) +var _ = error(Errno(0)) + +func (e Errno) Errno() Errno { + return e +} + +func (e Errno) String() string { + return syscall.Errno(e).Error() +} + +func (e Errno) Error() string { + return syscall.Errno(e).Error() +} + +// ErrnoName returns the short non-numeric identifier for this errno. +// For example, "EIO". +func (e Errno) ErrnoName() string { + s := errnoNames[e] + if s == "" { + s = fmt.Sprint(e.Errno()) + } + return s +} + +func (e Errno) MarshalText() ([]byte, error) { + s := e.ErrnoName() + return []byte(s), nil +} + +func (h *Header) RespondError(err error) { + errno := DefaultErrno + if ferr, ok := err.(ErrorNumber); ok { + errno = ferr.Errno() + } + // FUSE uses negative errors! + // TODO: File bug report against OSXFUSE: positive error causes kernel panic. + buf := newBuffer(0) + hOut := (*outHeader)(unsafe.Pointer(&buf[0])) + hOut.Error = -int32(errno) + h.respond(buf) +} + +// All requests read from the kernel, without data, are shorter than +// this. +var maxRequestSize = syscall.Getpagesize() +var bufSize = maxRequestSize + maxWrite + +// reqPool is a pool of messages. +// +// Lifetime of a logical message is from getMessage to putMessage. +// getMessage is called by ReadRequest. putMessage is called by +// Conn.ReadRequest, Request.Respond, or Request.RespondError. +// +// Messages in the pool are guaranteed to have conn and off zeroed, +// buf allocated and len==bufSize, and hdr set. +var reqPool = sync.Pool{ + New: allocMessage, +} + +func allocMessage() interface{} { + m := &message{buf: make([]byte, bufSize)} + m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0])) + return m +} + +func getMessage(c *Conn) *message { + m := reqPool.Get().(*message) + m.conn = c + return m +} + +func putMessage(m *message) { + m.buf = m.buf[:bufSize] + m.conn = nil + m.off = 0 + reqPool.Put(m) +} + +// a message represents the bytes of a single FUSE message +type message struct { + conn *Conn + buf []byte // all bytes + hdr *inHeader // header + off int // offset for reading additional fields +} + +func (m *message) len() uintptr { + return uintptr(len(m.buf) - m.off) +} + +func (m *message) data() unsafe.Pointer { + var p unsafe.Pointer + if m.off < len(m.buf) { + p = unsafe.Pointer(&m.buf[m.off]) + } + return p +} + +func (m *message) bytes() []byte { + return m.buf[m.off:] +} + +func (m *message) Header() Header { + h := m.hdr + return Header{ + Conn: m.conn, + ID: RequestID(h.Unique), + Node: NodeID(h.Nodeid), + Uid: h.Uid, + Gid: h.Gid, + Pid: h.Pid, + + msg: m, + } +} + +// fileMode returns a Go os.FileMode from a Unix mode. +func fileMode(unixMode uint32) os.FileMode { + mode := os.FileMode(unixMode & 0777) + switch unixMode & syscall.S_IFMT { + case syscall.S_IFREG: + // nothing + case syscall.S_IFDIR: + mode |= os.ModeDir + case syscall.S_IFCHR: + mode |= os.ModeCharDevice | os.ModeDevice + case syscall.S_IFBLK: + mode |= os.ModeDevice + case syscall.S_IFIFO: + mode |= os.ModeNamedPipe + case syscall.S_IFLNK: + mode |= os.ModeSymlink + case syscall.S_IFSOCK: + mode |= os.ModeSocket + default: + // no idea + mode |= os.ModeDevice + } + if unixMode&syscall.S_ISUID != 0 { + mode |= os.ModeSetuid + } + if unixMode&syscall.S_ISGID != 0 { + mode |= os.ModeSetgid + } + return mode +} + +type noOpcode struct { + Opcode uint32 +} + +func (m noOpcode) String() string { + return fmt.Sprintf("No opcode %v", m.Opcode) +} + +type malformedMessage struct { +} + +func (malformedMessage) String() string { + return "malformed message" +} + +// Close closes the FUSE connection. +func (c *Conn) Close() error { + c.wio.Lock() + defer c.wio.Unlock() + c.rio.Lock() + defer c.rio.Unlock() + return c.dev.Close() +} + +// caller must hold wio or rio +func (c *Conn) fd() int { + return int(c.dev.Fd()) +} + +func (c *Conn) Protocol() Protocol { + return c.proto +} + +// ReadRequest returns the next FUSE request from the kernel. +// +// Caller must call either Request.Respond or Request.RespondError in +// a reasonable time. Caller must not retain Request after that call. +func (c *Conn) ReadRequest() (Request, error) { + m := getMessage(c) +loop: + c.rio.RLock() + n, err := syscall.Read(c.fd(), m.buf) + c.rio.RUnlock() + if err == syscall.EINTR { + // OSXFUSE sends EINTR to userspace when a request interrupt + // completed before it got sent to userspace? + goto loop + } + if err != nil && err != syscall.ENODEV { + putMessage(m) + return nil, err + } + if n <= 0 { + putMessage(m) + return nil, io.EOF + } + m.buf = m.buf[:n] + + if n < inHeaderSize { + putMessage(m) + return nil, errors.New("fuse: message too short") + } + + // FreeBSD FUSE sends a short length in the header + // for FUSE_INIT even though the actual read length is correct. + if n == inHeaderSize+initInSize && m.hdr.Opcode == opInit && m.hdr.Len < uint32(n) { + m.hdr.Len = uint32(n) + } + + // OSXFUSE sometimes sends the wrong m.hdr.Len in a FUSE_WRITE message. + if m.hdr.Len < uint32(n) && m.hdr.Len >= uint32(unsafe.Sizeof(writeIn{})) && m.hdr.Opcode == opWrite { + m.hdr.Len = uint32(n) + } + + if m.hdr.Len != uint32(n) { + // prepare error message before returning m to pool + err := fmt.Errorf("fuse: read %d opcode %d but expected %d", n, m.hdr.Opcode, m.hdr.Len) + putMessage(m) + return nil, err + } + + m.off = inHeaderSize + + // Convert to data structures. + // Do not trust kernel to hand us well-formed data. + var req Request + switch m.hdr.Opcode { + default: + Debug(noOpcode{Opcode: m.hdr.Opcode}) + goto unrecognized + + case opLookup: + buf := m.bytes() + n := len(buf) + if n == 0 || buf[n-1] != '\x00' { + goto corrupt + } + req = &LookupRequest{ + Header: m.Header(), + Name: string(buf[:n-1]), + } + + case opForget: + in := (*forgetIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &ForgetRequest{ + Header: m.Header(), + N: in.Nlookup, + } + + case opGetattr: + switch { + case c.proto.LT(Protocol{7, 9}): + req = &GetattrRequest{ + Header: m.Header(), + } + + default: + in := (*getattrIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &GetattrRequest{ + Header: m.Header(), + Flags: GetattrFlags(in.GetattrFlags), + Handle: HandleID(in.Fh), + } + } + + case opSetattr: + in := (*setattrIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &SetattrRequest{ + Header: m.Header(), + Valid: SetattrValid(in.Valid), + Handle: HandleID(in.Fh), + Size: in.Size, + Atime: time.Unix(int64(in.Atime), int64(in.AtimeNsec)), + Mtime: time.Unix(int64(in.Mtime), int64(in.MtimeNsec)), + Mode: fileMode(in.Mode), + Uid: in.Uid, + Gid: in.Gid, + Bkuptime: in.BkupTime(), + Chgtime: in.Chgtime(), + Flags: in.Flags(), + } + + case opReadlink: + if len(m.bytes()) > 0 { + goto corrupt + } + req = &ReadlinkRequest{ + Header: m.Header(), + } + + case opSymlink: + // m.bytes() is "newName\0target\0" + names := m.bytes() + if len(names) == 0 || names[len(names)-1] != 0 { + goto corrupt + } + i := bytes.IndexByte(names, '\x00') + if i < 0 { + goto corrupt + } + newName, target := names[0:i], names[i+1:len(names)-1] + req = &SymlinkRequest{ + Header: m.Header(), + NewName: string(newName), + Target: string(target), + } + + case opLink: + in := (*linkIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + newName := m.bytes()[unsafe.Sizeof(*in):] + if len(newName) < 2 || newName[len(newName)-1] != 0 { + goto corrupt + } + newName = newName[:len(newName)-1] + req = &LinkRequest{ + Header: m.Header(), + OldNode: NodeID(in.Oldnodeid), + NewName: string(newName), + } + + case opMknod: + size := mknodInSize(c.proto) + if m.len() < size { + goto corrupt + } + in := (*mknodIn)(m.data()) + name := m.bytes()[size:] + if len(name) < 2 || name[len(name)-1] != '\x00' { + goto corrupt + } + name = name[:len(name)-1] + r := &MknodRequest{ + Header: m.Header(), + Mode: fileMode(in.Mode), + Rdev: in.Rdev, + Name: string(name), + } + if c.proto.GE(Protocol{7, 12}) { + r.Umask = fileMode(in.Umask) & os.ModePerm + } + req = r + + case opMkdir: + size := mkdirInSize(c.proto) + if m.len() < size { + goto corrupt + } + in := (*mkdirIn)(m.data()) + name := m.bytes()[size:] + i := bytes.IndexByte(name, '\x00') + if i < 0 { + goto corrupt + } + r := &MkdirRequest{ + Header: m.Header(), + Name: string(name[:i]), + // observed on Linux: mkdirIn.Mode & syscall.S_IFMT == 0, + // and this causes fileMode to go into it's "no idea" + // code branch; enforce type to directory + Mode: fileMode((in.Mode &^ syscall.S_IFMT) | syscall.S_IFDIR), + } + if c.proto.GE(Protocol{7, 12}) { + r.Umask = fileMode(in.Umask) & os.ModePerm + } + req = r + + case opUnlink, opRmdir: + buf := m.bytes() + n := len(buf) + if n == 0 || buf[n-1] != '\x00' { + goto corrupt + } + req = &RemoveRequest{ + Header: m.Header(), + Name: string(buf[:n-1]), + Dir: m.hdr.Opcode == opRmdir, + } + + case opRename: + in := (*renameIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + newDirNodeID := NodeID(in.Newdir) + oldNew := m.bytes()[unsafe.Sizeof(*in):] + // oldNew should be "old\x00new\x00" + if len(oldNew) < 4 { + goto corrupt + } + if oldNew[len(oldNew)-1] != '\x00' { + goto corrupt + } + i := bytes.IndexByte(oldNew, '\x00') + if i < 0 { + goto corrupt + } + oldName, newName := string(oldNew[:i]), string(oldNew[i+1:len(oldNew)-1]) + req = &RenameRequest{ + Header: m.Header(), + NewDir: newDirNodeID, + OldName: oldName, + NewName: newName, + } + + case opOpendir, opOpen: + in := (*openIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &OpenRequest{ + Header: m.Header(), + Dir: m.hdr.Opcode == opOpendir, + Flags: openFlags(in.Flags), + } + + case opRead, opReaddir: + in := (*readIn)(m.data()) + if m.len() < readInSize(c.proto) { + goto corrupt + } + r := &ReadRequest{ + Header: m.Header(), + Dir: m.hdr.Opcode == opReaddir, + Handle: HandleID(in.Fh), + Offset: int64(in.Offset), + Size: int(in.Size), + } + if c.proto.GE(Protocol{7, 9}) { + r.Flags = ReadFlags(in.ReadFlags) + r.LockOwner = in.LockOwner + r.FileFlags = openFlags(in.Flags) + } + req = r + + case opWrite: + in := (*writeIn)(m.data()) + if m.len() < writeInSize(c.proto) { + goto corrupt + } + r := &WriteRequest{ + Header: m.Header(), + Handle: HandleID(in.Fh), + Offset: int64(in.Offset), + Flags: WriteFlags(in.WriteFlags), + } + if c.proto.GE(Protocol{7, 9}) { + r.LockOwner = in.LockOwner + r.FileFlags = openFlags(in.Flags) + } + buf := m.bytes()[writeInSize(c.proto):] + if uint32(len(buf)) < in.Size { + goto corrupt + } + r.Data = buf + req = r + + case opStatfs: + req = &StatfsRequest{ + Header: m.Header(), + } + + case opRelease, opReleasedir: + in := (*releaseIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &ReleaseRequest{ + Header: m.Header(), + Dir: m.hdr.Opcode == opReleasedir, + Handle: HandleID(in.Fh), + Flags: openFlags(in.Flags), + ReleaseFlags: ReleaseFlags(in.ReleaseFlags), + LockOwner: in.LockOwner, + } + + case opFsync, opFsyncdir: + in := (*fsyncIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &FsyncRequest{ + Dir: m.hdr.Opcode == opFsyncdir, + Header: m.Header(), + Handle: HandleID(in.Fh), + Flags: in.FsyncFlags, + } + + case opSetxattr: + in := (*setxattrIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + m.off += int(unsafe.Sizeof(*in)) + name := m.bytes() + i := bytes.IndexByte(name, '\x00') + if i < 0 { + goto corrupt + } + xattr := name[i+1:] + if uint32(len(xattr)) < in.Size { + goto corrupt + } + xattr = xattr[:in.Size] + req = &SetxattrRequest{ + Header: m.Header(), + Flags: in.Flags, + Position: in.position(), + Name: string(name[:i]), + Xattr: xattr, + } + + case opGetxattr: + in := (*getxattrIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + name := m.bytes()[unsafe.Sizeof(*in):] + i := bytes.IndexByte(name, '\x00') + if i < 0 { + goto corrupt + } + req = &GetxattrRequest{ + Header: m.Header(), + Name: string(name[:i]), + Size: in.Size, + Position: in.position(), + } + + case opListxattr: + in := (*getxattrIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &ListxattrRequest{ + Header: m.Header(), + Size: in.Size, + Position: in.position(), + } + + case opRemovexattr: + buf := m.bytes() + n := len(buf) + if n == 0 || buf[n-1] != '\x00' { + goto corrupt + } + req = &RemovexattrRequest{ + Header: m.Header(), + Name: string(buf[:n-1]), + } + + case opFlush: + in := (*flushIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &FlushRequest{ + Header: m.Header(), + Handle: HandleID(in.Fh), + Flags: in.FlushFlags, + LockOwner: in.LockOwner, + } + + case opInit: + in := (*initIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &InitRequest{ + Header: m.Header(), + Kernel: Protocol{in.Major, in.Minor}, + MaxReadahead: in.MaxReadahead, + Flags: InitFlags(in.Flags), + } + + case opGetlk: + panic("opGetlk") + case opSetlk: + panic("opSetlk") + case opSetlkw: + panic("opSetlkw") + + case opAccess: + in := (*accessIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &AccessRequest{ + Header: m.Header(), + Mask: in.Mask, + } + + case opCreate: + size := createInSize(c.proto) + if m.len() < size { + goto corrupt + } + in := (*createIn)(m.data()) + name := m.bytes()[size:] + i := bytes.IndexByte(name, '\x00') + if i < 0 { + goto corrupt + } + r := &CreateRequest{ + Header: m.Header(), + Flags: openFlags(in.Flags), + Mode: fileMode(in.Mode), + Name: string(name[:i]), + } + if c.proto.GE(Protocol{7, 12}) { + r.Umask = fileMode(in.Umask) & os.ModePerm + } + req = r + + case opInterrupt: + in := (*interruptIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + req = &InterruptRequest{ + Header: m.Header(), + IntrID: RequestID(in.Unique), + } + + case opBmap: + panic("opBmap") + + case opDestroy: + req = &DestroyRequest{ + Header: m.Header(), + } + + // OS X + case opSetvolname: + panic("opSetvolname") + case opGetxtimes: + panic("opGetxtimes") + case opExchange: + in := (*exchangeIn)(m.data()) + if m.len() < unsafe.Sizeof(*in) { + goto corrupt + } + oldDirNodeID := NodeID(in.Olddir) + newDirNodeID := NodeID(in.Newdir) + oldNew := m.bytes()[unsafe.Sizeof(*in):] + // oldNew should be "oldname\x00newname\x00" + if len(oldNew) < 4 { + goto corrupt + } + if oldNew[len(oldNew)-1] != '\x00' { + goto corrupt + } + i := bytes.IndexByte(oldNew, '\x00') + if i < 0 { + goto corrupt + } + oldName, newName := string(oldNew[:i]), string(oldNew[i+1:len(oldNew)-1]) + req = &ExchangeDataRequest{ + Header: m.Header(), + OldDir: oldDirNodeID, + NewDir: newDirNodeID, + OldName: oldName, + NewName: newName, + // TODO options + } + } + + return req, nil + +corrupt: + Debug(malformedMessage{}) + putMessage(m) + return nil, fmt.Errorf("fuse: malformed message") + +unrecognized: + // Unrecognized message. + // Assume higher-level code will send a "no idea what you mean" error. + h := m.Header() + return &h, nil +} + +type bugShortKernelWrite struct { + Written int64 + Length int64 + Error string + Stack string +} + +func (b bugShortKernelWrite) String() string { + return fmt.Sprintf("short kernel write: written=%d/%d error=%q stack=\n%s", b.Written, b.Length, b.Error, b.Stack) +} + +type bugKernelWriteError struct { + Error string + Stack string +} + +func (b bugKernelWriteError) String() string { + return fmt.Sprintf("kernel write error: error=%q stack=\n%s", b.Error, b.Stack) +} + +// safe to call even with nil error +func errorString(err error) string { + if err == nil { + return "" + } + return err.Error() +} + +func (c *Conn) writeToKernel(msg []byte) error { + out := (*outHeader)(unsafe.Pointer(&msg[0])) + out.Len = uint32(len(msg)) + + c.wio.RLock() + defer c.wio.RUnlock() + nn, err := syscall.Write(c.fd(), msg) + if err == nil && nn != len(msg) { + Debug(bugShortKernelWrite{ + Written: int64(nn), + Length: int64(len(msg)), + Error: errorString(err), + Stack: stack(), + }) + } + return err +} + +func (c *Conn) respond(msg []byte) { + if err := c.writeToKernel(msg); err != nil { + Debug(bugKernelWriteError{ + Error: errorString(err), + Stack: stack(), + }) + } +} + +type notCachedError struct{} + +func (notCachedError) Error() string { + return "node not cached" +} + +var _ ErrorNumber = notCachedError{} + +func (notCachedError) Errno() Errno { + // Behave just like if the original syscall.ENOENT had been passed + // straight through. + return ENOENT +} + +var ( + ErrNotCached = notCachedError{} +) + +// sendInvalidate sends an invalidate notification to kernel. +// +// A returned ENOENT is translated to a friendlier error. +func (c *Conn) sendInvalidate(msg []byte) error { + switch err := c.writeToKernel(msg); err { + case syscall.ENOENT: + return ErrNotCached + default: + return err + } +} + +// InvalidateNode invalidates the kernel cache of the attributes and a +// range of the data of a node. +// +// Giving offset 0 and size -1 means all data. To invalidate just the +// attributes, give offset 0 and size 0. +// +// Returns ErrNotCached if the kernel is not currently caching the +// node. +func (c *Conn) InvalidateNode(nodeID NodeID, off int64, size int64) error { + buf := newBuffer(unsafe.Sizeof(notifyInvalInodeOut{})) + h := (*outHeader)(unsafe.Pointer(&buf[0])) + // h.Unique is 0 + h.Error = notifyCodeInvalInode + out := (*notifyInvalInodeOut)(buf.alloc(unsafe.Sizeof(notifyInvalInodeOut{}))) + out.Ino = uint64(nodeID) + out.Off = off + out.Len = size + return c.sendInvalidate(buf) +} + +// InvalidateEntry invalidates the kernel cache of the directory entry +// identified by parent directory node ID and entry basename. +// +// Kernel may or may not cache directory listings. To invalidate +// those, use InvalidateNode to invalidate all of the data for a +// directory. (As of 2015-06, Linux FUSE does not cache directory +// listings.) +// +// Returns ErrNotCached if the kernel is not currently caching the +// node. +func (c *Conn) InvalidateEntry(parent NodeID, name string) error { + const maxUint32 = ^uint32(0) + if uint64(len(name)) > uint64(maxUint32) { + // very unlikely, but we don't want to silently truncate + return syscall.ENAMETOOLONG + } + buf := newBuffer(unsafe.Sizeof(notifyInvalEntryOut{}) + uintptr(len(name)) + 1) + h := (*outHeader)(unsafe.Pointer(&buf[0])) + // h.Unique is 0 + h.Error = notifyCodeInvalEntry + out := (*notifyInvalEntryOut)(buf.alloc(unsafe.Sizeof(notifyInvalEntryOut{}))) + out.Parent = uint64(parent) + out.Namelen = uint32(len(name)) + buf = append(buf, name...) + buf = append(buf, '\x00') + return c.sendInvalidate(buf) +} + +// An InitRequest is the first request sent on a FUSE file system. +type InitRequest struct { + Header `json:"-"` + Kernel Protocol + // Maximum readahead in bytes that the kernel plans to use. + MaxReadahead uint32 + Flags InitFlags +} + +var _ = Request(&InitRequest{}) + +func (r *InitRequest) String() string { + return fmt.Sprintf("Init [%v] %v ra=%d fl=%v", &r.Header, r.Kernel, r.MaxReadahead, r.Flags) +} + +// An InitResponse is the response to an InitRequest. +type InitResponse struct { + Library Protocol + // Maximum readahead in bytes that the kernel can use. Ignored if + // greater than InitRequest.MaxReadahead. + MaxReadahead uint32 + Flags InitFlags + // Maximum size of a single write operation. + // Linux enforces a minimum of 4 KiB. + MaxWrite uint32 +} + +func (r *InitResponse) String() string { + return fmt.Sprintf("Init %v ra=%d fl=%v w=%d", r.Library, r.MaxReadahead, r.Flags, r.MaxWrite) +} + +// Respond replies to the request with the given response. +func (r *InitRequest) Respond(resp *InitResponse) { + buf := newBuffer(unsafe.Sizeof(initOut{})) + out := (*initOut)(buf.alloc(unsafe.Sizeof(initOut{}))) + out.Major = resp.Library.Major + out.Minor = resp.Library.Minor + out.MaxReadahead = resp.MaxReadahead + out.Flags = uint32(resp.Flags) + out.MaxWrite = resp.MaxWrite + + // MaxWrite larger than our receive buffer would just lead to + // errors on large writes. + if out.MaxWrite > maxWrite { + out.MaxWrite = maxWrite + } + r.respond(buf) +} + +// A StatfsRequest requests information about the mounted file system. +type StatfsRequest struct { + Header `json:"-"` +} + +var _ = Request(&StatfsRequest{}) + +func (r *StatfsRequest) String() string { + return fmt.Sprintf("Statfs [%s]", &r.Header) +} + +// Respond replies to the request with the given response. +func (r *StatfsRequest) Respond(resp *StatfsResponse) { + buf := newBuffer(unsafe.Sizeof(statfsOut{})) + out := (*statfsOut)(buf.alloc(unsafe.Sizeof(statfsOut{}))) + out.St = kstatfs{ + Blocks: resp.Blocks, + Bfree: resp.Bfree, + Bavail: resp.Bavail, + Files: resp.Files, + Bsize: resp.Bsize, + Namelen: resp.Namelen, + Frsize: resp.Frsize, + } + r.respond(buf) +} + +// A StatfsResponse is the response to a StatfsRequest. +type StatfsResponse struct { + Blocks uint64 // Total data blocks in file system. + Bfree uint64 // Free blocks in file system. + Bavail uint64 // Free blocks in file system if you're not root. + Files uint64 // Total files in file system. + Ffree uint64 // Free files in file system. + Bsize uint32 // Block size + Namelen uint32 // Maximum file name length? + Frsize uint32 // Fragment size, smallest addressable data size in the file system. +} + +func (r *StatfsResponse) String() string { + return fmt.Sprintf("Statfs blocks=%d/%d/%d files=%d/%d bsize=%d frsize=%d namelen=%d", + r.Bavail, r.Bfree, r.Blocks, + r.Ffree, r.Files, + r.Bsize, + r.Frsize, + r.Namelen, + ) +} + +// An AccessRequest asks whether the file can be accessed +// for the purpose specified by the mask. +type AccessRequest struct { + Header `json:"-"` + Mask uint32 +} + +var _ = Request(&AccessRequest{}) + +func (r *AccessRequest) String() string { + return fmt.Sprintf("Access [%s] mask=%#x", &r.Header, r.Mask) +} + +// Respond replies to the request indicating that access is allowed. +// To deny access, use RespondError. +func (r *AccessRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +// An Attr is the metadata for a single file or directory. +type Attr struct { + Valid time.Duration // how long Attr can be cached + + Inode uint64 // inode number + Size uint64 // size in bytes + Blocks uint64 // size in 512-byte units + Atime time.Time // time of last access + Mtime time.Time // time of last modification + Ctime time.Time // time of last inode change + Crtime time.Time // time of creation (OS X only) + Mode os.FileMode // file mode + Nlink uint32 // number of links (usually 1) + Uid uint32 // owner uid + Gid uint32 // group gid + Rdev uint32 // device numbers + Flags uint32 // chflags(2) flags (OS X only) + BlockSize uint32 // preferred blocksize for filesystem I/O +} + +func (a Attr) String() string { + return fmt.Sprintf("valid=%v ino=%v size=%d mode=%v", a.Valid, a.Inode, a.Size, a.Mode) +} + +func unix(t time.Time) (sec uint64, nsec uint32) { + nano := t.UnixNano() + sec = uint64(nano / 1e9) + nsec = uint32(nano % 1e9) + return +} + +func (a *Attr) attr(out *attr, proto Protocol) { + out.Ino = a.Inode + out.Size = a.Size + out.Blocks = a.Blocks + out.Atime, out.AtimeNsec = unix(a.Atime) + out.Mtime, out.MtimeNsec = unix(a.Mtime) + out.Ctime, out.CtimeNsec = unix(a.Ctime) + out.SetCrtime(unix(a.Crtime)) + out.Mode = uint32(a.Mode) & 0777 + switch { + default: + out.Mode |= syscall.S_IFREG + case a.Mode&os.ModeDir != 0: + out.Mode |= syscall.S_IFDIR + case a.Mode&os.ModeDevice != 0: + if a.Mode&os.ModeCharDevice != 0 { + out.Mode |= syscall.S_IFCHR + } else { + out.Mode |= syscall.S_IFBLK + } + case a.Mode&os.ModeNamedPipe != 0: + out.Mode |= syscall.S_IFIFO + case a.Mode&os.ModeSymlink != 0: + out.Mode |= syscall.S_IFLNK + case a.Mode&os.ModeSocket != 0: + out.Mode |= syscall.S_IFSOCK + } + if a.Mode&os.ModeSetuid != 0 { + out.Mode |= syscall.S_ISUID + } + if a.Mode&os.ModeSetgid != 0 { + out.Mode |= syscall.S_ISGID + } + out.Nlink = a.Nlink + out.Uid = a.Uid + out.Gid = a.Gid + out.Rdev = a.Rdev + out.SetFlags(a.Flags) + if proto.GE(Protocol{7, 9}) { + out.Blksize = a.BlockSize + } + + return +} + +// A GetattrRequest asks for the metadata for the file denoted by r.Node. +type GetattrRequest struct { + Header `json:"-"` + Flags GetattrFlags + Handle HandleID +} + +var _ = Request(&GetattrRequest{}) + +func (r *GetattrRequest) String() string { + return fmt.Sprintf("Getattr [%s] %v fl=%v", &r.Header, r.Handle, r.Flags) +} + +// Respond replies to the request with the given response. +func (r *GetattrRequest) Respond(resp *GetattrResponse) { + size := attrOutSize(r.Header.Conn.proto) + buf := newBuffer(size) + out := (*attrOut)(buf.alloc(size)) + out.AttrValid = uint64(resp.Attr.Valid / time.Second) + out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) + resp.Attr.attr(&out.Attr, r.Header.Conn.proto) + r.respond(buf) +} + +// A GetattrResponse is the response to a GetattrRequest. +type GetattrResponse struct { + Attr Attr // file attributes +} + +func (r *GetattrResponse) String() string { + return fmt.Sprintf("Getattr %v", r.Attr) +} + +// A GetxattrRequest asks for the extended attributes associated with r.Node. +type GetxattrRequest struct { + Header `json:"-"` + + // Maximum size to return. + Size uint32 + + // Name of the attribute requested. + Name string + + // Offset within extended attributes. + // + // Only valid for OS X, and then only with the resource fork + // attribute. + Position uint32 +} + +var _ = Request(&GetxattrRequest{}) + +func (r *GetxattrRequest) String() string { + return fmt.Sprintf("Getxattr [%s] %q %d @%d", &r.Header, r.Name, r.Size, r.Position) +} + +// Respond replies to the request with the given response. +func (r *GetxattrRequest) Respond(resp *GetxattrResponse) { + if r.Size == 0 { + buf := newBuffer(unsafe.Sizeof(getxattrOut{})) + out := (*getxattrOut)(buf.alloc(unsafe.Sizeof(getxattrOut{}))) + out.Size = uint32(len(resp.Xattr)) + r.respond(buf) + } else { + buf := newBuffer(uintptr(len(resp.Xattr))) + buf = append(buf, resp.Xattr...) + r.respond(buf) + } +} + +// A GetxattrResponse is the response to a GetxattrRequest. +type GetxattrResponse struct { + Xattr []byte +} + +func (r *GetxattrResponse) String() string { + return fmt.Sprintf("Getxattr %x", r.Xattr) +} + +// A ListxattrRequest asks to list the extended attributes associated with r.Node. +type ListxattrRequest struct { + Header `json:"-"` + Size uint32 // maximum size to return + Position uint32 // offset within attribute list +} + +var _ = Request(&ListxattrRequest{}) + +func (r *ListxattrRequest) String() string { + return fmt.Sprintf("Listxattr [%s] %d @%d", &r.Header, r.Size, r.Position) +} + +// Respond replies to the request with the given response. +func (r *ListxattrRequest) Respond(resp *ListxattrResponse) { + if r.Size == 0 { + buf := newBuffer(unsafe.Sizeof(getxattrOut{})) + out := (*getxattrOut)(buf.alloc(unsafe.Sizeof(getxattrOut{}))) + out.Size = uint32(len(resp.Xattr)) + r.respond(buf) + } else { + buf := newBuffer(uintptr(len(resp.Xattr))) + buf = append(buf, resp.Xattr...) + r.respond(buf) + } +} + +// A ListxattrResponse is the response to a ListxattrRequest. +type ListxattrResponse struct { + Xattr []byte +} + +func (r *ListxattrResponse) String() string { + return fmt.Sprintf("Listxattr %x", r.Xattr) +} + +// Append adds an extended attribute name to the response. +func (r *ListxattrResponse) Append(names ...string) { + for _, name := range names { + r.Xattr = append(r.Xattr, name...) + r.Xattr = append(r.Xattr, '\x00') + } +} + +// A RemovexattrRequest asks to remove an extended attribute associated with r.Node. +type RemovexattrRequest struct { + Header `json:"-"` + Name string // name of extended attribute +} + +var _ = Request(&RemovexattrRequest{}) + +func (r *RemovexattrRequest) String() string { + return fmt.Sprintf("Removexattr [%s] %q", &r.Header, r.Name) +} + +// Respond replies to the request, indicating that the attribute was removed. +func (r *RemovexattrRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +// A SetxattrRequest asks to set an extended attribute associated with a file. +type SetxattrRequest struct { + Header `json:"-"` + + // Flags can make the request fail if attribute does/not already + // exist. Unfortunately, the constants are platform-specific and + // not exposed by Go1.2. Look for XATTR_CREATE, XATTR_REPLACE. + // + // TODO improve this later + // + // TODO XATTR_CREATE and exist -> EEXIST + // + // TODO XATTR_REPLACE and not exist -> ENODATA + Flags uint32 + + // Offset within extended attributes. + // + // Only valid for OS X, and then only with the resource fork + // attribute. + Position uint32 + + Name string + Xattr []byte +} + +var _ = Request(&SetxattrRequest{}) + +func trunc(b []byte, max int) ([]byte, string) { + if len(b) > max { + return b[:max], "..." + } + return b, "" +} + +func (r *SetxattrRequest) String() string { + xattr, tail := trunc(r.Xattr, 16) + return fmt.Sprintf("Setxattr [%s] %q %x%s fl=%v @%#x", &r.Header, r.Name, xattr, tail, r.Flags, r.Position) +} + +// Respond replies to the request, indicating that the extended attribute was set. +func (r *SetxattrRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +// A LookupRequest asks to look up the given name in the directory named by r.Node. +type LookupRequest struct { + Header `json:"-"` + Name string +} + +var _ = Request(&LookupRequest{}) + +func (r *LookupRequest) String() string { + return fmt.Sprintf("Lookup [%s] %q", &r.Header, r.Name) +} + +// Respond replies to the request with the given response. +func (r *LookupRequest) Respond(resp *LookupResponse) { + size := entryOutSize(r.Header.Conn.proto) + buf := newBuffer(size) + out := (*entryOut)(buf.alloc(size)) + out.Nodeid = uint64(resp.Node) + out.Generation = resp.Generation + out.EntryValid = uint64(resp.EntryValid / time.Second) + out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) + out.AttrValid = uint64(resp.Attr.Valid / time.Second) + out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) + resp.Attr.attr(&out.Attr, r.Header.Conn.proto) + r.respond(buf) +} + +// A LookupResponse is the response to a LookupRequest. +type LookupResponse struct { + Node NodeID + Generation uint64 + EntryValid time.Duration + Attr Attr +} + +func (r *LookupResponse) string() string { + return fmt.Sprintf("%v gen=%d valid=%v attr={%v}", r.Node, r.Generation, r.EntryValid, r.Attr) +} + +func (r *LookupResponse) String() string { + return fmt.Sprintf("Lookup %s", r.string()) +} + +// An OpenRequest asks to open a file or directory +type OpenRequest struct { + Header `json:"-"` + Dir bool // is this Opendir? + Flags OpenFlags +} + +var _ = Request(&OpenRequest{}) + +func (r *OpenRequest) String() string { + return fmt.Sprintf("Open [%s] dir=%v fl=%v", &r.Header, r.Dir, r.Flags) +} + +// Respond replies to the request with the given response. +func (r *OpenRequest) Respond(resp *OpenResponse) { + buf := newBuffer(unsafe.Sizeof(openOut{})) + out := (*openOut)(buf.alloc(unsafe.Sizeof(openOut{}))) + out.Fh = uint64(resp.Handle) + out.OpenFlags = uint32(resp.Flags) + r.respond(buf) +} + +// A OpenResponse is the response to a OpenRequest. +type OpenResponse struct { + Handle HandleID + Flags OpenResponseFlags +} + +func (r *OpenResponse) string() string { + return fmt.Sprintf("%v fl=%v", r.Handle, r.Flags) +} + +func (r *OpenResponse) String() string { + return fmt.Sprintf("Open %s", r.string()) +} + +// A CreateRequest asks to create and open a file (not a directory). +type CreateRequest struct { + Header `json:"-"` + Name string + Flags OpenFlags + Mode os.FileMode + // Umask of the request. Not supported on OS X. + Umask os.FileMode +} + +var _ = Request(&CreateRequest{}) + +func (r *CreateRequest) String() string { + return fmt.Sprintf("Create [%s] %q fl=%v mode=%v umask=%v", &r.Header, r.Name, r.Flags, r.Mode, r.Umask) +} + +// Respond replies to the request with the given response. +func (r *CreateRequest) Respond(resp *CreateResponse) { + eSize := entryOutSize(r.Header.Conn.proto) + buf := newBuffer(eSize + unsafe.Sizeof(openOut{})) + + e := (*entryOut)(buf.alloc(eSize)) + e.Nodeid = uint64(resp.Node) + e.Generation = resp.Generation + e.EntryValid = uint64(resp.EntryValid / time.Second) + e.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) + e.AttrValid = uint64(resp.Attr.Valid / time.Second) + e.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) + resp.Attr.attr(&e.Attr, r.Header.Conn.proto) + + o := (*openOut)(buf.alloc(unsafe.Sizeof(openOut{}))) + o.Fh = uint64(resp.Handle) + o.OpenFlags = uint32(resp.Flags) + + r.respond(buf) +} + +// A CreateResponse is the response to a CreateRequest. +// It describes the created node and opened handle. +type CreateResponse struct { + LookupResponse + OpenResponse +} + +func (r *CreateResponse) String() string { + return fmt.Sprintf("Create {%s} {%s}", r.LookupResponse.string(), r.OpenResponse.string()) +} + +// A MkdirRequest asks to create (but not open) a directory. +type MkdirRequest struct { + Header `json:"-"` + Name string + Mode os.FileMode + // Umask of the request. Not supported on OS X. + Umask os.FileMode +} + +var _ = Request(&MkdirRequest{}) + +func (r *MkdirRequest) String() string { + return fmt.Sprintf("Mkdir [%s] %q mode=%v umask=%v", &r.Header, r.Name, r.Mode, r.Umask) +} + +// Respond replies to the request with the given response. +func (r *MkdirRequest) Respond(resp *MkdirResponse) { + size := entryOutSize(r.Header.Conn.proto) + buf := newBuffer(size) + out := (*entryOut)(buf.alloc(size)) + out.Nodeid = uint64(resp.Node) + out.Generation = resp.Generation + out.EntryValid = uint64(resp.EntryValid / time.Second) + out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) + out.AttrValid = uint64(resp.Attr.Valid / time.Second) + out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) + resp.Attr.attr(&out.Attr, r.Header.Conn.proto) + r.respond(buf) +} + +// A MkdirResponse is the response to a MkdirRequest. +type MkdirResponse struct { + LookupResponse +} + +func (r *MkdirResponse) String() string { + return fmt.Sprintf("Mkdir %v", r.LookupResponse.string()) +} + +// A ReadRequest asks to read from an open file. +type ReadRequest struct { + Header `json:"-"` + Dir bool // is this Readdir? + Handle HandleID + Offset int64 + Size int + Flags ReadFlags + LockOwner uint64 + FileFlags OpenFlags +} + +var _ = Request(&ReadRequest{}) + +func (r *ReadRequest) String() string { + return fmt.Sprintf("Read [%s] %v %d @%#x dir=%v fl=%v lock=%d ffl=%v", &r.Header, r.Handle, r.Size, r.Offset, r.Dir, r.Flags, r.LockOwner, r.FileFlags) +} + +// Respond replies to the request with the given response. +func (r *ReadRequest) Respond(resp *ReadResponse) { + buf := newBuffer(uintptr(len(resp.Data))) + buf = append(buf, resp.Data...) + r.respond(buf) +} + +// A ReadResponse is the response to a ReadRequest. +type ReadResponse struct { + Data []byte +} + +func (r *ReadResponse) String() string { + return fmt.Sprintf("Read %d", len(r.Data)) +} + +type jsonReadResponse struct { + Len uint64 +} + +func (r *ReadResponse) MarshalJSON() ([]byte, error) { + j := jsonReadResponse{ + Len: uint64(len(r.Data)), + } + return json.Marshal(j) +} + +// A ReleaseRequest asks to release (close) an open file handle. +type ReleaseRequest struct { + Header `json:"-"` + Dir bool // is this Releasedir? + Handle HandleID + Flags OpenFlags // flags from OpenRequest + ReleaseFlags ReleaseFlags + LockOwner uint32 +} + +var _ = Request(&ReleaseRequest{}) + +func (r *ReleaseRequest) String() string { + return fmt.Sprintf("Release [%s] %v fl=%v rfl=%v owner=%#x", &r.Header, r.Handle, r.Flags, r.ReleaseFlags, r.LockOwner) +} + +// Respond replies to the request, indicating that the handle has been released. +func (r *ReleaseRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +// A DestroyRequest is sent by the kernel when unmounting the file system. +// No more requests will be received after this one, but it should still be +// responded to. +type DestroyRequest struct { + Header `json:"-"` +} + +var _ = Request(&DestroyRequest{}) + +func (r *DestroyRequest) String() string { + return fmt.Sprintf("Destroy [%s]", &r.Header) +} + +// Respond replies to the request. +func (r *DestroyRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +// A ForgetRequest is sent by the kernel when forgetting about r.Node +// as returned by r.N lookup requests. +type ForgetRequest struct { + Header `json:"-"` + N uint64 +} + +var _ = Request(&ForgetRequest{}) + +func (r *ForgetRequest) String() string { + return fmt.Sprintf("Forget [%s] %d", &r.Header, r.N) +} + +// Respond replies to the request, indicating that the forgetfulness has been recorded. +func (r *ForgetRequest) Respond() { + // Don't reply to forget messages. + r.noResponse() +} + +// A Dirent represents a single directory entry. +type Dirent struct { + // Inode this entry names. + Inode uint64 + + // Type of the entry, for example DT_File. + // + // Setting this is optional. The zero value (DT_Unknown) means + // callers will just need to do a Getattr when the type is + // needed. Providing a type can speed up operations + // significantly. + Type DirentType + + // Name of the entry + Name string +} + +// Type of an entry in a directory listing. +type DirentType uint32 + +const ( + // These don't quite match os.FileMode; especially there's an + // explicit unknown, instead of zero value meaning file. They + // are also not quite syscall.DT_*; nothing says the FUSE + // protocol follows those, and even if they were, we don't + // want each fs to fiddle with syscall. + + // The shift by 12 is hardcoded in the FUSE userspace + // low-level C library, so it's safe here. + + DT_Unknown DirentType = 0 + DT_Socket DirentType = syscall.S_IFSOCK >> 12 + DT_Link DirentType = syscall.S_IFLNK >> 12 + DT_File DirentType = syscall.S_IFREG >> 12 + DT_Block DirentType = syscall.S_IFBLK >> 12 + DT_Dir DirentType = syscall.S_IFDIR >> 12 + DT_Char DirentType = syscall.S_IFCHR >> 12 + DT_FIFO DirentType = syscall.S_IFIFO >> 12 +) + +func (t DirentType) String() string { + switch t { + case DT_Unknown: + return "unknown" + case DT_Socket: + return "socket" + case DT_Link: + return "link" + case DT_File: + return "file" + case DT_Block: + return "block" + case DT_Dir: + return "dir" + case DT_Char: + return "char" + case DT_FIFO: + return "fifo" + } + return "invalid" +} + +// AppendDirent appends the encoded form of a directory entry to data +// and returns the resulting slice. +func AppendDirent(data []byte, dir Dirent) []byte { + de := dirent{ + Ino: dir.Inode, + Namelen: uint32(len(dir.Name)), + Type: uint32(dir.Type), + } + de.Off = uint64(len(data) + direntSize + (len(dir.Name)+7)&^7) + data = append(data, (*[direntSize]byte)(unsafe.Pointer(&de))[:]...) + data = append(data, dir.Name...) + n := direntSize + uintptr(len(dir.Name)) + if n%8 != 0 { + var pad [8]byte + data = append(data, pad[:8-n%8]...) + } + return data +} + +// A WriteRequest asks to write to an open file. +type WriteRequest struct { + Header + Handle HandleID + Offset int64 + Data []byte + Flags WriteFlags + LockOwner uint64 + FileFlags OpenFlags +} + +var _ = Request(&WriteRequest{}) + +func (r *WriteRequest) String() string { + return fmt.Sprintf("Write [%s] %v %d @%d fl=%v lock=%d ffl=%v", &r.Header, r.Handle, len(r.Data), r.Offset, r.Flags, r.LockOwner, r.FileFlags) +} + +type jsonWriteRequest struct { + Handle HandleID + Offset int64 + Len uint64 + Flags WriteFlags +} + +func (r *WriteRequest) MarshalJSON() ([]byte, error) { + j := jsonWriteRequest{ + Handle: r.Handle, + Offset: r.Offset, + Len: uint64(len(r.Data)), + Flags: r.Flags, + } + return json.Marshal(j) +} + +// Respond replies to the request with the given response. +func (r *WriteRequest) Respond(resp *WriteResponse) { + buf := newBuffer(unsafe.Sizeof(writeOut{})) + out := (*writeOut)(buf.alloc(unsafe.Sizeof(writeOut{}))) + out.Size = uint32(resp.Size) + r.respond(buf) +} + +// A WriteResponse replies to a write indicating how many bytes were written. +type WriteResponse struct { + Size int +} + +func (r *WriteResponse) String() string { + return fmt.Sprintf("Write %d", r.Size) +} + +// A SetattrRequest asks to change one or more attributes associated with a file, +// as indicated by Valid. +type SetattrRequest struct { + Header `json:"-"` + Valid SetattrValid + Handle HandleID + Size uint64 + Atime time.Time + Mtime time.Time + Mode os.FileMode + Uid uint32 + Gid uint32 + + // OS X only + Bkuptime time.Time + Chgtime time.Time + Crtime time.Time + Flags uint32 // see chflags(2) +} + +var _ = Request(&SetattrRequest{}) + +func (r *SetattrRequest) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "Setattr [%s]", &r.Header) + if r.Valid.Mode() { + fmt.Fprintf(&buf, " mode=%v", r.Mode) + } + if r.Valid.Uid() { + fmt.Fprintf(&buf, " uid=%d", r.Uid) + } + if r.Valid.Gid() { + fmt.Fprintf(&buf, " gid=%d", r.Gid) + } + if r.Valid.Size() { + fmt.Fprintf(&buf, " size=%d", r.Size) + } + if r.Valid.Atime() { + fmt.Fprintf(&buf, " atime=%v", r.Atime) + } + if r.Valid.AtimeNow() { + fmt.Fprintf(&buf, " atime=now") + } + if r.Valid.Mtime() { + fmt.Fprintf(&buf, " mtime=%v", r.Mtime) + } + if r.Valid.MtimeNow() { + fmt.Fprintf(&buf, " mtime=now") + } + if r.Valid.Handle() { + fmt.Fprintf(&buf, " handle=%v", r.Handle) + } else { + fmt.Fprintf(&buf, " handle=INVALID-%v", r.Handle) + } + if r.Valid.LockOwner() { + fmt.Fprintf(&buf, " lockowner") + } + if r.Valid.Crtime() { + fmt.Fprintf(&buf, " crtime=%v", r.Crtime) + } + if r.Valid.Chgtime() { + fmt.Fprintf(&buf, " chgtime=%v", r.Chgtime) + } + if r.Valid.Bkuptime() { + fmt.Fprintf(&buf, " bkuptime=%v", r.Bkuptime) + } + if r.Valid.Flags() { + fmt.Fprintf(&buf, " flags=%v", r.Flags) + } + return buf.String() +} + +// Respond replies to the request with the given response, +// giving the updated attributes. +func (r *SetattrRequest) Respond(resp *SetattrResponse) { + size := attrOutSize(r.Header.Conn.proto) + buf := newBuffer(size) + out := (*attrOut)(buf.alloc(size)) + out.AttrValid = uint64(resp.Attr.Valid / time.Second) + out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) + resp.Attr.attr(&out.Attr, r.Header.Conn.proto) + r.respond(buf) +} + +// A SetattrResponse is the response to a SetattrRequest. +type SetattrResponse struct { + Attr Attr // file attributes +} + +func (r *SetattrResponse) String() string { + return fmt.Sprintf("Setattr %v", r.Attr) +} + +// A FlushRequest asks for the current state of an open file to be flushed +// to storage, as when a file descriptor is being closed. A single opened Handle +// may receive multiple FlushRequests over its lifetime. +type FlushRequest struct { + Header `json:"-"` + Handle HandleID + Flags uint32 + LockOwner uint64 +} + +var _ = Request(&FlushRequest{}) + +func (r *FlushRequest) String() string { + return fmt.Sprintf("Flush [%s] %v fl=%#x lk=%#x", &r.Header, r.Handle, r.Flags, r.LockOwner) +} + +// Respond replies to the request, indicating that the flush succeeded. +func (r *FlushRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +// A RemoveRequest asks to remove a file or directory from the +// directory r.Node. +type RemoveRequest struct { + Header `json:"-"` + Name string // name of the entry to remove + Dir bool // is this rmdir? +} + +var _ = Request(&RemoveRequest{}) + +func (r *RemoveRequest) String() string { + return fmt.Sprintf("Remove [%s] %q dir=%v", &r.Header, r.Name, r.Dir) +} + +// Respond replies to the request, indicating that the file was removed. +func (r *RemoveRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +// A SymlinkRequest is a request to create a symlink making NewName point to Target. +type SymlinkRequest struct { + Header `json:"-"` + NewName, Target string +} + +var _ = Request(&SymlinkRequest{}) + +func (r *SymlinkRequest) String() string { + return fmt.Sprintf("Symlink [%s] from %q to target %q", &r.Header, r.NewName, r.Target) +} + +// Respond replies to the request, indicating that the symlink was created. +func (r *SymlinkRequest) Respond(resp *SymlinkResponse) { + size := entryOutSize(r.Header.Conn.proto) + buf := newBuffer(size) + out := (*entryOut)(buf.alloc(size)) + out.Nodeid = uint64(resp.Node) + out.Generation = resp.Generation + out.EntryValid = uint64(resp.EntryValid / time.Second) + out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) + out.AttrValid = uint64(resp.Attr.Valid / time.Second) + out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) + resp.Attr.attr(&out.Attr, r.Header.Conn.proto) + r.respond(buf) +} + +// A SymlinkResponse is the response to a SymlinkRequest. +type SymlinkResponse struct { + LookupResponse +} + +func (r *SymlinkResponse) String() string { + return fmt.Sprintf("Symlink %v", r.LookupResponse.string()) +} + +// A ReadlinkRequest is a request to read a symlink's target. +type ReadlinkRequest struct { + Header `json:"-"` +} + +var _ = Request(&ReadlinkRequest{}) + +func (r *ReadlinkRequest) String() string { + return fmt.Sprintf("Readlink [%s]", &r.Header) +} + +func (r *ReadlinkRequest) Respond(target string) { + buf := newBuffer(uintptr(len(target))) + buf = append(buf, target...) + r.respond(buf) +} + +// A LinkRequest is a request to create a hard link. +type LinkRequest struct { + Header `json:"-"` + OldNode NodeID + NewName string +} + +var _ = Request(&LinkRequest{}) + +func (r *LinkRequest) String() string { + return fmt.Sprintf("Link [%s] node %d to %q", &r.Header, r.OldNode, r.NewName) +} + +func (r *LinkRequest) Respond(resp *LookupResponse) { + size := entryOutSize(r.Header.Conn.proto) + buf := newBuffer(size) + out := (*entryOut)(buf.alloc(size)) + out.Nodeid = uint64(resp.Node) + out.Generation = resp.Generation + out.EntryValid = uint64(resp.EntryValid / time.Second) + out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) + out.AttrValid = uint64(resp.Attr.Valid / time.Second) + out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) + resp.Attr.attr(&out.Attr, r.Header.Conn.proto) + r.respond(buf) +} + +// A RenameRequest is a request to rename a file. +type RenameRequest struct { + Header `json:"-"` + NewDir NodeID + OldName, NewName string +} + +var _ = Request(&RenameRequest{}) + +func (r *RenameRequest) String() string { + return fmt.Sprintf("Rename [%s] from %q to dirnode %v %q", &r.Header, r.OldName, r.NewDir, r.NewName) +} + +func (r *RenameRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +type MknodRequest struct { + Header `json:"-"` + Name string + Mode os.FileMode + Rdev uint32 + // Umask of the request. Not supported on OS X. + Umask os.FileMode +} + +var _ = Request(&MknodRequest{}) + +func (r *MknodRequest) String() string { + return fmt.Sprintf("Mknod [%s] Name %q mode=%v umask=%v rdev=%d", &r.Header, r.Name, r.Mode, r.Umask, r.Rdev) +} + +func (r *MknodRequest) Respond(resp *LookupResponse) { + size := entryOutSize(r.Header.Conn.proto) + buf := newBuffer(size) + out := (*entryOut)(buf.alloc(size)) + out.Nodeid = uint64(resp.Node) + out.Generation = resp.Generation + out.EntryValid = uint64(resp.EntryValid / time.Second) + out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) + out.AttrValid = uint64(resp.Attr.Valid / time.Second) + out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) + resp.Attr.attr(&out.Attr, r.Header.Conn.proto) + r.respond(buf) +} + +type FsyncRequest struct { + Header `json:"-"` + Handle HandleID + // TODO bit 1 is datasync, not well documented upstream + Flags uint32 + Dir bool +} + +var _ = Request(&FsyncRequest{}) + +func (r *FsyncRequest) String() string { + return fmt.Sprintf("Fsync [%s] Handle %v Flags %v", &r.Header, r.Handle, r.Flags) +} + +func (r *FsyncRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} + +// An InterruptRequest is a request to interrupt another pending request. The +// response to that request should return an error status of EINTR. +type InterruptRequest struct { + Header `json:"-"` + IntrID RequestID // ID of the request to be interrupt. +} + +var _ = Request(&InterruptRequest{}) + +func (r *InterruptRequest) Respond() { + // nothing to do here + r.noResponse() +} + +func (r *InterruptRequest) String() string { + return fmt.Sprintf("Interrupt [%s] ID %v", &r.Header, r.IntrID) +} + +// An ExchangeDataRequest is a request to exchange the contents of two +// files, while leaving most metadata untouched. +// +// This request comes from OS X exchangedata(2) and represents its +// specific semantics. Crucially, it is very different from Linux +// renameat(2) RENAME_EXCHANGE. +// +// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/exchangedata.2.html +type ExchangeDataRequest struct { + Header `json:"-"` + OldDir, NewDir NodeID + OldName, NewName string + // TODO options +} + +var _ = Request(&ExchangeDataRequest{}) + +func (r *ExchangeDataRequest) String() string { + // TODO options + return fmt.Sprintf("ExchangeData [%s] %v %q and %v %q", &r.Header, r.OldDir, r.OldName, r.NewDir, r.NewName) +} + +func (r *ExchangeDataRequest) Respond() { + buf := newBuffer(0) + r.respond(buf) +} diff --git a/vendor/bazil.org/fuse/fuse.iml b/vendor/bazil.org/fuse/fuse.iml new file mode 100644 index 000000000..792ad4c30 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse.iml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="GO_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$" /> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/vendor/bazil.org/fuse/fuse_darwin.go b/vendor/bazil.org/fuse/fuse_darwin.go new file mode 100644 index 000000000..b58dca97d --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_darwin.go @@ -0,0 +1,9 @@ +package fuse + +// Maximum file write size we are prepared to receive from the kernel. +// +// This value has to be >=16MB or OSXFUSE (3.4.0 observed) will +// forcibly close the /dev/fuse file descriptor on a Setxattr with a +// 16MB value. See TestSetxattr16MB and +// https://github.com/bazil/fuse/issues/42 +const maxWrite = 16 * 1024 * 1024 diff --git a/vendor/bazil.org/fuse/fuse_freebsd.go b/vendor/bazil.org/fuse/fuse_freebsd.go new file mode 100644 index 000000000..4aa83a0d4 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_freebsd.go @@ -0,0 +1,6 @@ +package fuse + +// Maximum file write size we are prepared to receive from the kernel. +// +// This number is just a guess. +const maxWrite = 128 * 1024 diff --git a/vendor/bazil.org/fuse/fuse_kernel.go b/vendor/bazil.org/fuse/fuse_kernel.go new file mode 100644 index 000000000..87c5ca1dc --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_kernel.go @@ -0,0 +1,774 @@ +// See the file LICENSE for copyright and licensing information. + +// Derived from FUSE's fuse_kernel.h, which carries this notice: +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +package fuse + +import ( + "fmt" + "syscall" + "unsafe" +) + +// The FUSE version implemented by the package. +const ( + protoVersionMinMajor = 7 + protoVersionMinMinor = 8 + protoVersionMaxMajor = 7 + protoVersionMaxMinor = 12 +) + +const ( + rootID = 1 +) + +type kstatfs struct { + Blocks uint64 + Bfree uint64 + Bavail uint64 + Files uint64 + Ffree uint64 + Bsize uint32 + Namelen uint32 + Frsize uint32 + _ uint32 + Spare [6]uint32 +} + +type fileLock struct { + Start uint64 + End uint64 + Type uint32 + Pid uint32 +} + +// GetattrFlags are bit flags that can be seen in GetattrRequest. +type GetattrFlags uint32 + +const ( + // Indicates the handle is valid. + GetattrFh GetattrFlags = 1 << 0 +) + +var getattrFlagsNames = []flagName{ + {uint32(GetattrFh), "GetattrFh"}, +} + +func (fl GetattrFlags) String() string { + return flagString(uint32(fl), getattrFlagsNames) +} + +// The SetattrValid are bit flags describing which fields in the SetattrRequest +// are included in the change. +type SetattrValid uint32 + +const ( + SetattrMode SetattrValid = 1 << 0 + SetattrUid SetattrValid = 1 << 1 + SetattrGid SetattrValid = 1 << 2 + SetattrSize SetattrValid = 1 << 3 + SetattrAtime SetattrValid = 1 << 4 + SetattrMtime SetattrValid = 1 << 5 + SetattrHandle SetattrValid = 1 << 6 + + // Linux only(?) + SetattrAtimeNow SetattrValid = 1 << 7 + SetattrMtimeNow SetattrValid = 1 << 8 + SetattrLockOwner SetattrValid = 1 << 9 // http://www.mail-archive.com/git-commits-head@vger.kernel.org/msg27852.html + + // OS X only + SetattrCrtime SetattrValid = 1 << 28 + SetattrChgtime SetattrValid = 1 << 29 + SetattrBkuptime SetattrValid = 1 << 30 + SetattrFlags SetattrValid = 1 << 31 +) + +func (fl SetattrValid) Mode() bool { return fl&SetattrMode != 0 } +func (fl SetattrValid) Uid() bool { return fl&SetattrUid != 0 } +func (fl SetattrValid) Gid() bool { return fl&SetattrGid != 0 } +func (fl SetattrValid) Size() bool { return fl&SetattrSize != 0 } +func (fl SetattrValid) Atime() bool { return fl&SetattrAtime != 0 } +func (fl SetattrValid) Mtime() bool { return fl&SetattrMtime != 0 } +func (fl SetattrValid) Handle() bool { return fl&SetattrHandle != 0 } +func (fl SetattrValid) AtimeNow() bool { return fl&SetattrAtimeNow != 0 } +func (fl SetattrValid) MtimeNow() bool { return fl&SetattrMtimeNow != 0 } +func (fl SetattrValid) LockOwner() bool { return fl&SetattrLockOwner != 0 } +func (fl SetattrValid) Crtime() bool { return fl&SetattrCrtime != 0 } +func (fl SetattrValid) Chgtime() bool { return fl&SetattrChgtime != 0 } +func (fl SetattrValid) Bkuptime() bool { return fl&SetattrBkuptime != 0 } +func (fl SetattrValid) Flags() bool { return fl&SetattrFlags != 0 } + +func (fl SetattrValid) String() string { + return flagString(uint32(fl), setattrValidNames) +} + +var setattrValidNames = []flagName{ + {uint32(SetattrMode), "SetattrMode"}, + {uint32(SetattrUid), "SetattrUid"}, + {uint32(SetattrGid), "SetattrGid"}, + {uint32(SetattrSize), "SetattrSize"}, + {uint32(SetattrAtime), "SetattrAtime"}, + {uint32(SetattrMtime), "SetattrMtime"}, + {uint32(SetattrHandle), "SetattrHandle"}, + {uint32(SetattrAtimeNow), "SetattrAtimeNow"}, + {uint32(SetattrMtimeNow), "SetattrMtimeNow"}, + {uint32(SetattrLockOwner), "SetattrLockOwner"}, + {uint32(SetattrCrtime), "SetattrCrtime"}, + {uint32(SetattrChgtime), "SetattrChgtime"}, + {uint32(SetattrBkuptime), "SetattrBkuptime"}, + {uint32(SetattrFlags), "SetattrFlags"}, +} + +// Flags that can be seen in OpenRequest.Flags. +const ( + // Access modes. These are not 1-bit flags, but alternatives where + // only one can be chosen. See the IsReadOnly etc convenience + // methods. + OpenReadOnly OpenFlags = syscall.O_RDONLY + OpenWriteOnly OpenFlags = syscall.O_WRONLY + OpenReadWrite OpenFlags = syscall.O_RDWR + + // File was opened in append-only mode, all writes will go to end + // of file. OS X does not provide this information. + OpenAppend OpenFlags = syscall.O_APPEND + OpenCreate OpenFlags = syscall.O_CREAT + OpenDirectory OpenFlags = syscall.O_DIRECTORY + OpenExclusive OpenFlags = syscall.O_EXCL + OpenNonblock OpenFlags = syscall.O_NONBLOCK + OpenSync OpenFlags = syscall.O_SYNC + OpenTruncate OpenFlags = syscall.O_TRUNC +) + +// OpenAccessModeMask is a bitmask that separates the access mode +// from the other flags in OpenFlags. +const OpenAccessModeMask OpenFlags = syscall.O_ACCMODE + +// OpenFlags are the O_FOO flags passed to open/create/etc calls. For +// example, os.O_WRONLY | os.O_APPEND. +type OpenFlags uint32 + +func (fl OpenFlags) String() string { + // O_RDONLY, O_RWONLY, O_RDWR are not flags + s := accModeName(fl & OpenAccessModeMask) + flags := uint32(fl &^ OpenAccessModeMask) + if flags != 0 { + s = s + "+" + flagString(flags, openFlagNames) + } + return s +} + +// Return true if OpenReadOnly is set. +func (fl OpenFlags) IsReadOnly() bool { + return fl&OpenAccessModeMask == OpenReadOnly +} + +// Return true if OpenWriteOnly is set. +func (fl OpenFlags) IsWriteOnly() bool { + return fl&OpenAccessModeMask == OpenWriteOnly +} + +// Return true if OpenReadWrite is set. +func (fl OpenFlags) IsReadWrite() bool { + return fl&OpenAccessModeMask == OpenReadWrite +} + +func accModeName(flags OpenFlags) string { + switch flags { + case OpenReadOnly: + return "OpenReadOnly" + case OpenWriteOnly: + return "OpenWriteOnly" + case OpenReadWrite: + return "OpenReadWrite" + default: + return "" + } +} + +var openFlagNames = []flagName{ + {uint32(OpenAppend), "OpenAppend"}, + {uint32(OpenCreate), "OpenCreate"}, + {uint32(OpenDirectory), "OpenDirectory"}, + {uint32(OpenExclusive), "OpenExclusive"}, + {uint32(OpenNonblock), "OpenNonblock"}, + {uint32(OpenSync), "OpenSync"}, + {uint32(OpenTruncate), "OpenTruncate"}, +} + +// The OpenResponseFlags are returned in the OpenResponse. +type OpenResponseFlags uint32 + +const ( + OpenDirectIO OpenResponseFlags = 1 << 0 // bypass page cache for this open file + OpenKeepCache OpenResponseFlags = 1 << 1 // don't invalidate the data cache on open + OpenNonSeekable OpenResponseFlags = 1 << 2 // mark the file as non-seekable (not supported on OS X) + + OpenPurgeAttr OpenResponseFlags = 1 << 30 // OS X + OpenPurgeUBC OpenResponseFlags = 1 << 31 // OS X +) + +func (fl OpenResponseFlags) String() string { + return flagString(uint32(fl), openResponseFlagNames) +} + +var openResponseFlagNames = []flagName{ + {uint32(OpenDirectIO), "OpenDirectIO"}, + {uint32(OpenKeepCache), "OpenKeepCache"}, + {uint32(OpenNonSeekable), "OpenNonSeekable"}, + {uint32(OpenPurgeAttr), "OpenPurgeAttr"}, + {uint32(OpenPurgeUBC), "OpenPurgeUBC"}, +} + +// The InitFlags are used in the Init exchange. +type InitFlags uint32 + +const ( + InitAsyncRead InitFlags = 1 << 0 + InitPosixLocks InitFlags = 1 << 1 + InitFileOps InitFlags = 1 << 2 + InitAtomicTrunc InitFlags = 1 << 3 + InitExportSupport InitFlags = 1 << 4 + InitBigWrites InitFlags = 1 << 5 + // Do not mask file access modes with umask. Not supported on OS X. + InitDontMask InitFlags = 1 << 6 + InitSpliceWrite InitFlags = 1 << 7 + InitSpliceMove InitFlags = 1 << 8 + InitSpliceRead InitFlags = 1 << 9 + InitFlockLocks InitFlags = 1 << 10 + InitHasIoctlDir InitFlags = 1 << 11 + InitAutoInvalData InitFlags = 1 << 12 + InitDoReaddirplus InitFlags = 1 << 13 + InitReaddirplusAuto InitFlags = 1 << 14 + InitAsyncDIO InitFlags = 1 << 15 + InitWritebackCache InitFlags = 1 << 16 + InitNoOpenSupport InitFlags = 1 << 17 + + InitCaseSensitive InitFlags = 1 << 29 // OS X only + InitVolRename InitFlags = 1 << 30 // OS X only + InitXtimes InitFlags = 1 << 31 // OS X only +) + +type flagName struct { + bit uint32 + name string +} + +var initFlagNames = []flagName{ + {uint32(InitAsyncRead), "InitAsyncRead"}, + {uint32(InitPosixLocks), "InitPosixLocks"}, + {uint32(InitFileOps), "InitFileOps"}, + {uint32(InitAtomicTrunc), "InitAtomicTrunc"}, + {uint32(InitExportSupport), "InitExportSupport"}, + {uint32(InitBigWrites), "InitBigWrites"}, + {uint32(InitDontMask), "InitDontMask"}, + {uint32(InitSpliceWrite), "InitSpliceWrite"}, + {uint32(InitSpliceMove), "InitSpliceMove"}, + {uint32(InitSpliceRead), "InitSpliceRead"}, + {uint32(InitFlockLocks), "InitFlockLocks"}, + {uint32(InitHasIoctlDir), "InitHasIoctlDir"}, + {uint32(InitAutoInvalData), "InitAutoInvalData"}, + {uint32(InitDoReaddirplus), "InitDoReaddirplus"}, + {uint32(InitReaddirplusAuto), "InitReaddirplusAuto"}, + {uint32(InitAsyncDIO), "InitAsyncDIO"}, + {uint32(InitWritebackCache), "InitWritebackCache"}, + {uint32(InitNoOpenSupport), "InitNoOpenSupport"}, + + {uint32(InitCaseSensitive), "InitCaseSensitive"}, + {uint32(InitVolRename), "InitVolRename"}, + {uint32(InitXtimes), "InitXtimes"}, +} + +func (fl InitFlags) String() string { + return flagString(uint32(fl), initFlagNames) +} + +func flagString(f uint32, names []flagName) string { + var s string + + if f == 0 { + return "0" + } + + for _, n := range names { + if f&n.bit != 0 { + s += "+" + n.name + f &^= n.bit + } + } + if f != 0 { + s += fmt.Sprintf("%+#x", f) + } + return s[1:] +} + +// The ReleaseFlags are used in the Release exchange. +type ReleaseFlags uint32 + +const ( + ReleaseFlush ReleaseFlags = 1 << 0 +) + +func (fl ReleaseFlags) String() string { + return flagString(uint32(fl), releaseFlagNames) +} + +var releaseFlagNames = []flagName{ + {uint32(ReleaseFlush), "ReleaseFlush"}, +} + +// Opcodes +const ( + opLookup = 1 + opForget = 2 // no reply + opGetattr = 3 + opSetattr = 4 + opReadlink = 5 + opSymlink = 6 + opMknod = 8 + opMkdir = 9 + opUnlink = 10 + opRmdir = 11 + opRename = 12 + opLink = 13 + opOpen = 14 + opRead = 15 + opWrite = 16 + opStatfs = 17 + opRelease = 18 + opFsync = 20 + opSetxattr = 21 + opGetxattr = 22 + opListxattr = 23 + opRemovexattr = 24 + opFlush = 25 + opInit = 26 + opOpendir = 27 + opReaddir = 28 + opReleasedir = 29 + opFsyncdir = 30 + opGetlk = 31 + opSetlk = 32 + opSetlkw = 33 + opAccess = 34 + opCreate = 35 + opInterrupt = 36 + opBmap = 37 + opDestroy = 38 + opIoctl = 39 // Linux? + opPoll = 40 // Linux? + + // OS X + opSetvolname = 61 + opGetxtimes = 62 + opExchange = 63 +) + +type entryOut struct { + Nodeid uint64 // Inode ID + Generation uint64 // Inode generation + EntryValid uint64 // Cache timeout for the name + AttrValid uint64 // Cache timeout for the attributes + EntryValidNsec uint32 + AttrValidNsec uint32 + Attr attr +} + +func entryOutSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 9}): + return unsafe.Offsetof(entryOut{}.Attr) + unsafe.Offsetof(entryOut{}.Attr.Blksize) + default: + return unsafe.Sizeof(entryOut{}) + } +} + +type forgetIn struct { + Nlookup uint64 +} + +type getattrIn struct { + GetattrFlags uint32 + _ uint32 + Fh uint64 +} + +type attrOut struct { + AttrValid uint64 // Cache timeout for the attributes + AttrValidNsec uint32 + _ uint32 + Attr attr +} + +func attrOutSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 9}): + return unsafe.Offsetof(attrOut{}.Attr) + unsafe.Offsetof(attrOut{}.Attr.Blksize) + default: + return unsafe.Sizeof(attrOut{}) + } +} + +// OS X +type getxtimesOut struct { + Bkuptime uint64 + Crtime uint64 + BkuptimeNsec uint32 + CrtimeNsec uint32 +} + +type mknodIn struct { + Mode uint32 + Rdev uint32 + Umask uint32 + _ uint32 + // "filename\x00" follows. +} + +func mknodInSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 12}): + return unsafe.Offsetof(mknodIn{}.Umask) + default: + return unsafe.Sizeof(mknodIn{}) + } +} + +type mkdirIn struct { + Mode uint32 + Umask uint32 + // filename follows +} + +func mkdirInSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 12}): + return unsafe.Offsetof(mkdirIn{}.Umask) + 4 + default: + return unsafe.Sizeof(mkdirIn{}) + } +} + +type renameIn struct { + Newdir uint64 + // "oldname\x00newname\x00" follows +} + +// OS X +type exchangeIn struct { + Olddir uint64 + Newdir uint64 + Options uint64 + // "oldname\x00newname\x00" follows +} + +type linkIn struct { + Oldnodeid uint64 +} + +type setattrInCommon struct { + Valid uint32 + _ uint32 + Fh uint64 + Size uint64 + LockOwner uint64 // unused on OS X? + Atime uint64 + Mtime uint64 + Unused2 uint64 + AtimeNsec uint32 + MtimeNsec uint32 + Unused3 uint32 + Mode uint32 + Unused4 uint32 + Uid uint32 + Gid uint32 + Unused5 uint32 +} + +type openIn struct { + Flags uint32 + Unused uint32 +} + +type openOut struct { + Fh uint64 + OpenFlags uint32 + _ uint32 +} + +type createIn struct { + Flags uint32 + Mode uint32 + Umask uint32 + _ uint32 +} + +func createInSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 12}): + return unsafe.Offsetof(createIn{}.Umask) + default: + return unsafe.Sizeof(createIn{}) + } +} + +type releaseIn struct { + Fh uint64 + Flags uint32 + ReleaseFlags uint32 + LockOwner uint32 +} + +type flushIn struct { + Fh uint64 + FlushFlags uint32 + _ uint32 + LockOwner uint64 +} + +type readIn struct { + Fh uint64 + Offset uint64 + Size uint32 + ReadFlags uint32 + LockOwner uint64 + Flags uint32 + _ uint32 +} + +func readInSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 9}): + return unsafe.Offsetof(readIn{}.ReadFlags) + 4 + default: + return unsafe.Sizeof(readIn{}) + } +} + +// The ReadFlags are passed in ReadRequest. +type ReadFlags uint32 + +const ( + // LockOwner field is valid. + ReadLockOwner ReadFlags = 1 << 1 +) + +var readFlagNames = []flagName{ + {uint32(ReadLockOwner), "ReadLockOwner"}, +} + +func (fl ReadFlags) String() string { + return flagString(uint32(fl), readFlagNames) +} + +type writeIn struct { + Fh uint64 + Offset uint64 + Size uint32 + WriteFlags uint32 + LockOwner uint64 + Flags uint32 + _ uint32 +} + +func writeInSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 9}): + return unsafe.Offsetof(writeIn{}.LockOwner) + default: + return unsafe.Sizeof(writeIn{}) + } +} + +type writeOut struct { + Size uint32 + _ uint32 +} + +// The WriteFlags are passed in WriteRequest. +type WriteFlags uint32 + +const ( + WriteCache WriteFlags = 1 << 0 + // LockOwner field is valid. + WriteLockOwner WriteFlags = 1 << 1 +) + +var writeFlagNames = []flagName{ + {uint32(WriteCache), "WriteCache"}, + {uint32(WriteLockOwner), "WriteLockOwner"}, +} + +func (fl WriteFlags) String() string { + return flagString(uint32(fl), writeFlagNames) +} + +const compatStatfsSize = 48 + +type statfsOut struct { + St kstatfs +} + +type fsyncIn struct { + Fh uint64 + FsyncFlags uint32 + _ uint32 +} + +type setxattrInCommon struct { + Size uint32 + Flags uint32 +} + +func (setxattrInCommon) position() uint32 { + return 0 +} + +type getxattrInCommon struct { + Size uint32 + _ uint32 +} + +func (getxattrInCommon) position() uint32 { + return 0 +} + +type getxattrOut struct { + Size uint32 + _ uint32 +} + +type lkIn struct { + Fh uint64 + Owner uint64 + Lk fileLock + LkFlags uint32 + _ uint32 +} + +func lkInSize(p Protocol) uintptr { + switch { + case p.LT(Protocol{7, 9}): + return unsafe.Offsetof(lkIn{}.LkFlags) + default: + return unsafe.Sizeof(lkIn{}) + } +} + +type lkOut struct { + Lk fileLock +} + +type accessIn struct { + Mask uint32 + _ uint32 +} + +type initIn struct { + Major uint32 + Minor uint32 + MaxReadahead uint32 + Flags uint32 +} + +const initInSize = int(unsafe.Sizeof(initIn{})) + +type initOut struct { + Major uint32 + Minor uint32 + MaxReadahead uint32 + Flags uint32 + Unused uint32 + MaxWrite uint32 +} + +type interruptIn struct { + Unique uint64 +} + +type bmapIn struct { + Block uint64 + BlockSize uint32 + _ uint32 +} + +type bmapOut struct { + Block uint64 +} + +type inHeader struct { + Len uint32 + Opcode uint32 + Unique uint64 + Nodeid uint64 + Uid uint32 + Gid uint32 + Pid uint32 + _ uint32 +} + +const inHeaderSize = int(unsafe.Sizeof(inHeader{})) + +type outHeader struct { + Len uint32 + Error int32 + Unique uint64 +} + +type dirent struct { + Ino uint64 + Off uint64 + Namelen uint32 + Type uint32 + Name [0]byte +} + +const direntSize = 8 + 8 + 4 + 4 + +const ( + notifyCodePoll int32 = 1 + notifyCodeInvalInode int32 = 2 + notifyCodeInvalEntry int32 = 3 +) + +type notifyInvalInodeOut struct { + Ino uint64 + Off int64 + Len int64 +} + +type notifyInvalEntryOut struct { + Parent uint64 + Namelen uint32 + _ uint32 +} diff --git a/vendor/bazil.org/fuse/fuse_kernel_darwin.go b/vendor/bazil.org/fuse/fuse_kernel_darwin.go new file mode 100644 index 000000000..b9873fdf3 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_kernel_darwin.go @@ -0,0 +1,88 @@ +package fuse + +import ( + "time" +) + +type attr struct { + Ino uint64 + Size uint64 + Blocks uint64 + Atime uint64 + Mtime uint64 + Ctime uint64 + Crtime_ uint64 // OS X only + AtimeNsec uint32 + MtimeNsec uint32 + CtimeNsec uint32 + CrtimeNsec uint32 // OS X only + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint32 + Flags_ uint32 // OS X only; see chflags(2) + Blksize uint32 + padding uint32 +} + +func (a *attr) SetCrtime(s uint64, ns uint32) { + a.Crtime_, a.CrtimeNsec = s, ns +} + +func (a *attr) SetFlags(f uint32) { + a.Flags_ = f +} + +type setattrIn struct { + setattrInCommon + + // OS X only + Bkuptime_ uint64 + Chgtime_ uint64 + Crtime uint64 + BkuptimeNsec uint32 + ChgtimeNsec uint32 + CrtimeNsec uint32 + Flags_ uint32 // see chflags(2) +} + +func (in *setattrIn) BkupTime() time.Time { + return time.Unix(int64(in.Bkuptime_), int64(in.BkuptimeNsec)) +} + +func (in *setattrIn) Chgtime() time.Time { + return time.Unix(int64(in.Chgtime_), int64(in.ChgtimeNsec)) +} + +func (in *setattrIn) Flags() uint32 { + return in.Flags_ +} + +func openFlags(flags uint32) OpenFlags { + return OpenFlags(flags) +} + +type getxattrIn struct { + getxattrInCommon + + // OS X only + Position uint32 + Padding uint32 +} + +func (g *getxattrIn) position() uint32 { + return g.Position +} + +type setxattrIn struct { + setxattrInCommon + + // OS X only + Position uint32 + Padding uint32 +} + +func (s *setxattrIn) position() uint32 { + return s.Position +} diff --git a/vendor/bazil.org/fuse/fuse_kernel_freebsd.go b/vendor/bazil.org/fuse/fuse_kernel_freebsd.go new file mode 100644 index 000000000..b1141e41d --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_kernel_freebsd.go @@ -0,0 +1,62 @@ +package fuse + +import "time" + +type attr struct { + Ino uint64 + Size uint64 + Blocks uint64 + Atime uint64 + Mtime uint64 + Ctime uint64 + AtimeNsec uint32 + MtimeNsec uint32 + CtimeNsec uint32 + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint32 + Blksize uint32 + padding uint32 +} + +func (a *attr) Crtime() time.Time { + return time.Time{} +} + +func (a *attr) SetCrtime(s uint64, ns uint32) { + // ignored on freebsd +} + +func (a *attr) SetFlags(f uint32) { + // ignored on freebsd +} + +type setattrIn struct { + setattrInCommon +} + +func (in *setattrIn) BkupTime() time.Time { + return time.Time{} +} + +func (in *setattrIn) Chgtime() time.Time { + return time.Time{} +} + +func (in *setattrIn) Flags() uint32 { + return 0 +} + +func openFlags(flags uint32) OpenFlags { + return OpenFlags(flags) +} + +type getxattrIn struct { + getxattrInCommon +} + +type setxattrIn struct { + setxattrInCommon +} diff --git a/vendor/bazil.org/fuse/fuse_kernel_linux.go b/vendor/bazil.org/fuse/fuse_kernel_linux.go new file mode 100644 index 000000000..d3ba86617 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_kernel_linux.go @@ -0,0 +1,70 @@ +package fuse + +import "time" + +type attr struct { + Ino uint64 + Size uint64 + Blocks uint64 + Atime uint64 + Mtime uint64 + Ctime uint64 + AtimeNsec uint32 + MtimeNsec uint32 + CtimeNsec uint32 + Mode uint32 + Nlink uint32 + Uid uint32 + Gid uint32 + Rdev uint32 + Blksize uint32 + padding uint32 +} + +func (a *attr) Crtime() time.Time { + return time.Time{} +} + +func (a *attr) SetCrtime(s uint64, ns uint32) { + // Ignored on Linux. +} + +func (a *attr) SetFlags(f uint32) { + // Ignored on Linux. +} + +type setattrIn struct { + setattrInCommon +} + +func (in *setattrIn) BkupTime() time.Time { + return time.Time{} +} + +func (in *setattrIn) Chgtime() time.Time { + return time.Time{} +} + +func (in *setattrIn) Flags() uint32 { + return 0 +} + +func openFlags(flags uint32) OpenFlags { + // on amd64, the 32-bit O_LARGEFILE flag is always seen; + // on i386, the flag probably depends on the app + // requesting, but in any case should be utterly + // uninteresting to us here; our kernel protocol messages + // are not directly related to the client app's kernel + // API/ABI + flags &^= 0x8000 + + return OpenFlags(flags) +} + +type getxattrIn struct { + getxattrInCommon +} + +type setxattrIn struct { + setxattrInCommon +} diff --git a/vendor/bazil.org/fuse/fuse_kernel_std.go b/vendor/bazil.org/fuse/fuse_kernel_std.go new file mode 100644 index 000000000..074cfd322 --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_kernel_std.go @@ -0,0 +1 @@ +package fuse diff --git a/vendor/bazil.org/fuse/fuse_linux.go b/vendor/bazil.org/fuse/fuse_linux.go new file mode 100644 index 000000000..5fb96f9ae --- /dev/null +++ b/vendor/bazil.org/fuse/fuse_linux.go @@ -0,0 +1,7 @@ +package fuse + +// Maximum file write size we are prepared to receive from the kernel. +// +// Linux 4.2.0 has been observed to cap this value at 128kB +// (FUSE_MAX_PAGES_PER_REQ=32, 4kB pages). +const maxWrite = 128 * 1024 diff --git a/vendor/bazil.org/fuse/fuseutil/fuseutil.go b/vendor/bazil.org/fuse/fuseutil/fuseutil.go new file mode 100644 index 000000000..b3f52b73b --- /dev/null +++ b/vendor/bazil.org/fuse/fuseutil/fuseutil.go @@ -0,0 +1,20 @@ +package fuseutil // import "bazil.org/fuse/fuseutil" + +import ( + "bazil.org/fuse" +) + +// HandleRead handles a read request assuming that data is the entire file content. +// It adjusts the amount returned in resp according to req.Offset and req.Size. +func HandleRead(req *fuse.ReadRequest, resp *fuse.ReadResponse, data []byte) { + if req.Offset >= int64(len(data)) { + data = nil + } else { + data = data[req.Offset:] + } + if len(data) > req.Size { + data = data[:req.Size] + } + n := copy(resp.Data[:req.Size], data) + resp.Data = resp.Data[:n] +} diff --git a/vendor/bazil.org/fuse/mount.go b/vendor/bazil.org/fuse/mount.go new file mode 100644 index 000000000..8054e9021 --- /dev/null +++ b/vendor/bazil.org/fuse/mount.go @@ -0,0 +1,38 @@ +package fuse + +import ( + "bufio" + "errors" + "io" + "log" + "sync" +) + +var ( + // ErrOSXFUSENotFound is returned from Mount when the OSXFUSE + // installation is not detected. + // + // Only happens on OS X. Make sure OSXFUSE is installed, or see + // OSXFUSELocations for customization. + ErrOSXFUSENotFound = errors.New("cannot locate OSXFUSE") +) + +func neverIgnoreLine(line string) bool { + return false +} + +func lineLogger(wg *sync.WaitGroup, prefix string, ignore func(line string) bool, r io.ReadCloser) { + defer wg.Done() + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + if ignore(line) { + continue + } + log.Printf("%s: %s", prefix, line) + } + if err := scanner.Err(); err != nil { + log.Printf("%s, error reading: %v", prefix, err) + } +} diff --git a/vendor/bazil.org/fuse/mount_darwin.go b/vendor/bazil.org/fuse/mount_darwin.go new file mode 100644 index 000000000..c1c36e62b --- /dev/null +++ b/vendor/bazil.org/fuse/mount_darwin.go @@ -0,0 +1,208 @@ +package fuse + +import ( + "errors" + "fmt" + "log" + "os" + "os/exec" + "path" + "strconv" + "strings" + "sync" + "syscall" +) + +var ( + errNoAvail = errors.New("no available fuse devices") + errNotLoaded = errors.New("osxfuse is not loaded") +) + +func loadOSXFUSE(bin string) error { + cmd := exec.Command(bin) + cmd.Dir = "/" + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + return err +} + +func openOSXFUSEDev(devPrefix string) (*os.File, error) { + var f *os.File + var err error + for i := uint64(0); ; i++ { + path := devPrefix + strconv.FormatUint(i, 10) + f, err = os.OpenFile(path, os.O_RDWR, 0000) + if os.IsNotExist(err) { + if i == 0 { + // not even the first device was found -> fuse is not loaded + return nil, errNotLoaded + } + + // we've run out of kernel-provided devices + return nil, errNoAvail + } + + if err2, ok := err.(*os.PathError); ok && err2.Err == syscall.EBUSY { + // try the next one + continue + } + + if err != nil { + return nil, err + } + return f, nil + } +} + +func handleMountOSXFUSE(helperName string, errCh chan<- error) func(line string) (ignore bool) { + var noMountpointPrefix = helperName + `: ` + const noMountpointSuffix = `: No such file or directory` + return func(line string) (ignore bool) { + if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) { + // re-extract it from the error message in case some layer + // changed the path + mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)] + err := &MountpointDoesNotExistError{ + Path: mountpoint, + } + select { + case errCh <- err: + return true + default: + // not the first error; fall back to logging it + return false + } + } + + return false + } +} + +// isBoringMountOSXFUSEError returns whether the Wait error is +// uninteresting; exit status 64 is. +func isBoringMountOSXFUSEError(err error) bool { + if err, ok := err.(*exec.ExitError); ok && err.Exited() { + if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 64 { + return true + } + } + return false +} + +func callMount(bin string, daemonVar string, dir string, conf *mountConfig, f *os.File, ready chan<- struct{}, errp *error) error { + for k, v := range conf.options { + if strings.Contains(k, ",") || strings.Contains(v, ",") { + // Silly limitation but the mount helper does not + // understand any escaping. See TestMountOptionCommaError. + return fmt.Errorf("mount options cannot contain commas on darwin: %q=%q", k, v) + } + } + cmd := exec.Command( + bin, + "-o", conf.getOptions(), + // Tell osxfuse-kext how large our buffer is. It must split + // writes larger than this into multiple writes. + // + // OSXFUSE seems to ignore InitResponse.MaxWrite, and uses + // this instead. + "-o", "iosize="+strconv.FormatUint(maxWrite, 10), + // refers to fd passed in cmd.ExtraFiles + "3", + dir, + ) + cmd.ExtraFiles = []*os.File{f} + cmd.Env = os.Environ() + // OSXFUSE <3.3.0 + cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=") + // OSXFUSE >=3.3.0 + cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=") + + daemon := os.Args[0] + if daemonVar != "" { + cmd.Env = append(cmd.Env, daemonVar+"="+daemon) + } + + stdout, err := cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("setting up mount_osxfusefs stderr: %v", err) + } + stderr, err := cmd.StderrPipe() + if err != nil { + return fmt.Errorf("setting up mount_osxfusefs stderr: %v", err) + } + + if err := cmd.Start(); err != nil { + return fmt.Errorf("mount_osxfusefs: %v", err) + } + helperErrCh := make(chan error, 1) + go func() { + var wg sync.WaitGroup + wg.Add(2) + go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout) + helperName := path.Base(bin) + go lineLogger(&wg, "mount helper error", handleMountOSXFUSE(helperName, helperErrCh), stderr) + wg.Wait() + if err := cmd.Wait(); err != nil { + // see if we have a better error to report + select { + case helperErr := <-helperErrCh: + // log the Wait error if it's not what we expected + if !isBoringMountOSXFUSEError(err) { + log.Printf("mount helper failed: %v", err) + } + // and now return what we grabbed from stderr as the real + // error + *errp = helperErr + close(ready) + return + default: + // nope, fall back to generic message + } + + *errp = fmt.Errorf("mount_osxfusefs: %v", err) + close(ready) + return + } + + *errp = nil + close(ready) + }() + return nil +} + +func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) { + locations := conf.osxfuseLocations + if locations == nil { + locations = []OSXFUSEPaths{ + OSXFUSELocationV3, + OSXFUSELocationV2, + } + } + for _, loc := range locations { + if _, err := os.Stat(loc.Mount); os.IsNotExist(err) { + // try the other locations + continue + } + + f, err := openOSXFUSEDev(loc.DevicePrefix) + if err == errNotLoaded { + err = loadOSXFUSE(loc.Load) + if err != nil { + return nil, err + } + // try again + f, err = openOSXFUSEDev(loc.DevicePrefix) + } + if err != nil { + return nil, err + } + err = callMount(loc.Mount, loc.DaemonVar, dir, conf, f, ready, errp) + if err != nil { + f.Close() + return nil, err + } + return f, nil + } + return nil, ErrOSXFUSENotFound +} diff --git a/vendor/bazil.org/fuse/mount_freebsd.go b/vendor/bazil.org/fuse/mount_freebsd.go new file mode 100644 index 000000000..70bb41024 --- /dev/null +++ b/vendor/bazil.org/fuse/mount_freebsd.go @@ -0,0 +1,111 @@ +package fuse + +import ( + "fmt" + "log" + "os" + "os/exec" + "strings" + "sync" + "syscall" +) + +func handleMountFusefsStderr(errCh chan<- error) func(line string) (ignore bool) { + return func(line string) (ignore bool) { + const ( + noMountpointPrefix = `mount_fusefs: ` + noMountpointSuffix = `: No such file or directory` + ) + if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) { + // re-extract it from the error message in case some layer + // changed the path + mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)] + err := &MountpointDoesNotExistError{ + Path: mountpoint, + } + select { + case errCh <- err: + return true + default: + // not the first error; fall back to logging it + return false + } + } + + return false + } +} + +// isBoringMountFusefsError returns whether the Wait error is +// uninteresting; exit status 1 is. +func isBoringMountFusefsError(err error) bool { + if err, ok := err.(*exec.ExitError); ok && err.Exited() { + if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 { + return true + } + } + return false +} + +func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) { + for k, v := range conf.options { + if strings.Contains(k, ",") || strings.Contains(v, ",") { + // Silly limitation but the mount helper does not + // understand any escaping. See TestMountOptionCommaError. + return nil, fmt.Errorf("mount options cannot contain commas on FreeBSD: %q=%q", k, v) + } + } + + f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0000) + if err != nil { + *errp = err + return nil, err + } + + cmd := exec.Command( + "/sbin/mount_fusefs", + "--safe", + "-o", conf.getOptions(), + "3", + dir, + ) + cmd.ExtraFiles = []*os.File{f} + + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err) + } + stderr, err := cmd.StderrPipe() + if err != nil { + return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err) + } + + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("mount_fusefs: %v", err) + } + helperErrCh := make(chan error, 1) + var wg sync.WaitGroup + wg.Add(2) + go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout) + go lineLogger(&wg, "mount helper error", handleMountFusefsStderr(helperErrCh), stderr) + wg.Wait() + if err := cmd.Wait(); err != nil { + // see if we have a better error to report + select { + case helperErr := <-helperErrCh: + // log the Wait error if it's not what we expected + if !isBoringMountFusefsError(err) { + log.Printf("mount helper failed: %v", err) + } + // and now return what we grabbed from stderr as the real + // error + return nil, helperErr + default: + // nope, fall back to generic message + } + return nil, fmt.Errorf("mount_fusefs: %v", err) + } + + close(ready) + return f, nil +} diff --git a/vendor/bazil.org/fuse/mount_linux.go b/vendor/bazil.org/fuse/mount_linux.go new file mode 100644 index 000000000..197d1044e --- /dev/null +++ b/vendor/bazil.org/fuse/mount_linux.go @@ -0,0 +1,150 @@ +package fuse + +import ( + "fmt" + "log" + "net" + "os" + "os/exec" + "strings" + "sync" + "syscall" +) + +func handleFusermountStderr(errCh chan<- error) func(line string) (ignore bool) { + return func(line string) (ignore bool) { + if line == `fusermount: failed to open /etc/fuse.conf: Permission denied` { + // Silence this particular message, it occurs way too + // commonly and isn't very relevant to whether the mount + // succeeds or not. + return true + } + + const ( + noMountpointPrefix = `fusermount: failed to access mountpoint ` + noMountpointSuffix = `: No such file or directory` + ) + if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) { + // re-extract it from the error message in case some layer + // changed the path + mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)] + err := &MountpointDoesNotExistError{ + Path: mountpoint, + } + select { + case errCh <- err: + return true + default: + // not the first error; fall back to logging it + return false + } + } + + return false + } +} + +// isBoringFusermountError returns whether the Wait error is +// uninteresting; exit status 1 is. +func isBoringFusermountError(err error) bool { + if err, ok := err.(*exec.ExitError); ok && err.Exited() { + if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 { + return true + } + } + return false +} + +func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) { + // linux mount is never delayed + close(ready) + + fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0) + if err != nil { + return nil, fmt.Errorf("socketpair error: %v", err) + } + + writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes") + defer writeFile.Close() + + readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads") + defer readFile.Close() + + cmd := exec.Command( + "fusermount", + "-o", conf.getOptions(), + "--", + dir, + ) + cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3") + + cmd.ExtraFiles = []*os.File{writeFile} + + var wg sync.WaitGroup + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("setting up fusermount stderr: %v", err) + } + stderr, err := cmd.StderrPipe() + if err != nil { + return nil, fmt.Errorf("setting up fusermount stderr: %v", err) + } + + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("fusermount: %v", err) + } + helperErrCh := make(chan error, 1) + wg.Add(2) + go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout) + go lineLogger(&wg, "mount helper error", handleFusermountStderr(helperErrCh), stderr) + wg.Wait() + if err := cmd.Wait(); err != nil { + // see if we have a better error to report + select { + case helperErr := <-helperErrCh: + // log the Wait error if it's not what we expected + if !isBoringFusermountError(err) { + log.Printf("mount helper failed: %v", err) + } + // and now return what we grabbed from stderr as the real + // error + return nil, helperErr + default: + // nope, fall back to generic message + } + + return nil, fmt.Errorf("fusermount: %v", err) + } + + c, err := net.FileConn(readFile) + if err != nil { + return nil, fmt.Errorf("FileConn from fusermount socket: %v", err) + } + defer c.Close() + + uc, ok := c.(*net.UnixConn) + if !ok { + return nil, fmt.Errorf("unexpected FileConn type; expected UnixConn, got %T", c) + } + + buf := make([]byte, 32) // expect 1 byte + oob := make([]byte, 32) // expect 24 bytes + _, oobn, _, _, err := uc.ReadMsgUnix(buf, oob) + scms, err := syscall.ParseSocketControlMessage(oob[:oobn]) + if err != nil { + return nil, fmt.Errorf("ParseSocketControlMessage: %v", err) + } + if len(scms) != 1 { + return nil, fmt.Errorf("expected 1 SocketControlMessage; got scms = %#v", scms) + } + scm := scms[0] + gotFds, err := syscall.ParseUnixRights(&scm) + if err != nil { + return nil, fmt.Errorf("syscall.ParseUnixRights: %v", err) + } + if len(gotFds) != 1 { + return nil, fmt.Errorf("wanted 1 fd; got %#v", gotFds) + } + f := os.NewFile(uintptr(gotFds[0]), "/dev/fuse") + return f, nil +} diff --git a/vendor/bazil.org/fuse/options.go b/vendor/bazil.org/fuse/options.go new file mode 100644 index 000000000..65ce8a541 --- /dev/null +++ b/vendor/bazil.org/fuse/options.go @@ -0,0 +1,310 @@ +package fuse + +import ( + "errors" + "strings" +) + +func dummyOption(conf *mountConfig) error { + return nil +} + +// mountConfig holds the configuration for a mount operation. +// Use it by passing MountOption values to Mount. +type mountConfig struct { + options map[string]string + maxReadahead uint32 + initFlags InitFlags + osxfuseLocations []OSXFUSEPaths +} + +func escapeComma(s string) string { + s = strings.Replace(s, `\`, `\\`, -1) + s = strings.Replace(s, `,`, `\,`, -1) + return s +} + +// getOptions makes a string of options suitable for passing to FUSE +// mount flag `-o`. Returns an empty string if no options were set. +// Any platform specific adjustments should happen before the call. +func (m *mountConfig) getOptions() string { + var opts []string + for k, v := range m.options { + k = escapeComma(k) + if v != "" { + k += "=" + escapeComma(v) + } + opts = append(opts, k) + } + return strings.Join(opts, ",") +} + +type mountOption func(*mountConfig) error + +// MountOption is passed to Mount to change the behavior of the mount. +type MountOption mountOption + +// FSName sets the file system name (also called source) that is +// visible in the list of mounted file systems. +// +// FreeBSD ignores this option. +func FSName(name string) MountOption { + return func(conf *mountConfig) error { + conf.options["fsname"] = name + return nil + } +} + +// Subtype sets the subtype of the mount. The main type is always +// `fuse`. The type in a list of mounted file systems will look like +// `fuse.foo`. +// +// OS X ignores this option. +// FreeBSD ignores this option. +func Subtype(fstype string) MountOption { + return func(conf *mountConfig) error { + conf.options["subtype"] = fstype + return nil + } +} + +// LocalVolume sets the volume to be local (instead of network), +// changing the behavior of Finder, Spotlight, and such. +// +// OS X only. Others ignore this option. +func LocalVolume() MountOption { + return localVolume +} + +// VolumeName sets the volume name shown in Finder. +// +// OS X only. Others ignore this option. +func VolumeName(name string) MountOption { + return volumeName(name) +} + +// NoAppleDouble makes OSXFUSE disallow files with names used by OS X +// to store extended attributes on file systems that do not support +// them natively. +// +// Such file names are: +// +// ._* +// .DS_Store +// +// OS X only. Others ignore this option. +func NoAppleDouble() MountOption { + return noAppleDouble +} + +// NoAppleXattr makes OSXFUSE disallow extended attributes with the +// prefix "com.apple.". This disables persistent Finder state and +// other such information. +// +// OS X only. Others ignore this option. +func NoAppleXattr() MountOption { + return noAppleXattr +} + +// ExclCreate causes O_EXCL flag to be set for only "truly" exclusive creates, +// i.e. create calls for which the initiator explicitly set the O_EXCL flag. +// +// OSXFUSE expects all create calls to return EEXIST in case the file +// already exists, regardless of whether O_EXCL was specified or not. +// To ensure this behavior, it normally sets OpenExclusive for all +// Create calls, regardless of whether the original call had it set. +// For distributed filesystems, that may force every file create to be +// a distributed consensus action, causing undesirable delays. +// +// This option makes the FUSE filesystem see the original flag value, +// and better decide when to ensure global consensus. +// +// Note that returning EEXIST on existing file create is still +// expected with OSXFUSE, regardless of the presence of the +// OpenExclusive flag. +// +// For more information, see +// https://github.com/osxfuse/osxfuse/issues/209 +// +// OS X only. Others ignore this options. +// Requires OSXFUSE 3.4.1 or newer. +func ExclCreate() MountOption { + return exclCreate +} + +// DaemonTimeout sets the time in seconds between a request and a reply before +// the FUSE mount is declared dead. +// +// OS X and FreeBSD only. Others ignore this option. +func DaemonTimeout(name string) MountOption { + return daemonTimeout(name) +} + +var ErrCannotCombineAllowOtherAndAllowRoot = errors.New("cannot combine AllowOther and AllowRoot") + +// AllowOther allows other users to access the file system. +// +// Only one of AllowOther or AllowRoot can be used. +func AllowOther() MountOption { + return func(conf *mountConfig) error { + if _, ok := conf.options["allow_root"]; ok { + return ErrCannotCombineAllowOtherAndAllowRoot + } + conf.options["allow_other"] = "" + return nil + } +} + +// AllowRoot allows other users to access the file system. +// +// Only one of AllowOther or AllowRoot can be used. +// +// FreeBSD ignores this option. +func AllowRoot() MountOption { + return func(conf *mountConfig) error { + if _, ok := conf.options["allow_other"]; ok { + return ErrCannotCombineAllowOtherAndAllowRoot + } + conf.options["allow_root"] = "" + return nil + } +} + +// AllowDev enables interpreting character or block special devices on the +// filesystem. +func AllowDev() MountOption { + return func(conf *mountConfig) error { + conf.options["dev"] = "" + return nil + } +} + +// AllowSUID allows set-user-identifier or set-group-identifier bits to take +// effect. +func AllowSUID() MountOption { + return func(conf *mountConfig) error { + conf.options["suid"] = "" + return nil + } +} + +// DefaultPermissions makes the kernel enforce access control based on +// the file mode (as in chmod). +// +// Without this option, the Node itself decides what is and is not +// allowed. This is normally ok because FUSE file systems cannot be +// accessed by other users without AllowOther/AllowRoot. +// +// FreeBSD ignores this option. +func DefaultPermissions() MountOption { + return func(conf *mountConfig) error { + conf.options["default_permissions"] = "" + return nil + } +} + +// ReadOnly makes the mount read-only. +func ReadOnly() MountOption { + return func(conf *mountConfig) error { + conf.options["ro"] = "" + return nil + } +} + +// MaxReadahead sets the number of bytes that can be prefetched for +// sequential reads. The kernel can enforce a maximum value lower than +// this. +// +// This setting makes the kernel perform speculative reads that do not +// originate from any client process. This usually tremendously +// improves read performance. +func MaxReadahead(n uint32) MountOption { + return func(conf *mountConfig) error { + conf.maxReadahead = n + return nil + } +} + +// AsyncRead enables multiple outstanding read requests for the same +// handle. Without this, there is at most one request in flight at a +// time. +func AsyncRead() MountOption { + return func(conf *mountConfig) error { + conf.initFlags |= InitAsyncRead + return nil + } +} + +// WritebackCache enables the kernel to buffer writes before sending +// them to the FUSE server. Without this, writethrough caching is +// used. +func WritebackCache() MountOption { + return func(conf *mountConfig) error { + conf.initFlags |= InitWritebackCache + return nil + } +} + +// OSXFUSEPaths describes the paths used by an installed OSXFUSE +// version. See OSXFUSELocationV3 for typical values. +type OSXFUSEPaths struct { + // Prefix for the device file. At mount time, an incrementing + // number is suffixed until a free FUSE device is found. + DevicePrefix string + // Path of the load helper, used to load the kernel extension if + // no device files are found. + Load string + // Path of the mount helper, used for the actual mount operation. + Mount string + // Environment variable used to pass the path to the executable + // calling the mount helper. + DaemonVar string +} + +// Default paths for OSXFUSE. See OSXFUSELocations. +var ( + OSXFUSELocationV3 = OSXFUSEPaths{ + DevicePrefix: "/dev/osxfuse", + Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse", + Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse", + DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH", + } + OSXFUSELocationV2 = OSXFUSEPaths{ + DevicePrefix: "/dev/osxfuse", + Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs", + Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", + DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH", + } +) + +// OSXFUSELocations sets where to look for OSXFUSE files. The +// arguments are all the possible locations. The previous locations +// are replaced. +// +// Without this option, OSXFUSELocationV3 and OSXFUSELocationV2 are +// used. +// +// OS X only. Others ignore this option. +func OSXFUSELocations(paths ...OSXFUSEPaths) MountOption { + return func(conf *mountConfig) error { + if len(paths) == 0 { + return errors.New("must specify at least one location for OSXFUSELocations") + } + // replace previous values, but make a copy so there's no + // worries about caller mutating their slice + conf.osxfuseLocations = append(conf.osxfuseLocations[:0], paths...) + return nil + } +} + +// AllowNonEmptyMount allows the mounting over a non-empty directory. +// +// The files in it will be shadowed by the freshly created mount. By +// default these mounts are rejected to prevent accidental covering up +// of data, which could for example prevent automatic backup. +func AllowNonEmptyMount() MountOption { + return func(conf *mountConfig) error { + conf.options["nonempty"] = "" + return nil + } +} diff --git a/vendor/bazil.org/fuse/options_darwin.go b/vendor/bazil.org/fuse/options_darwin.go new file mode 100644 index 000000000..faa9d78e7 --- /dev/null +++ b/vendor/bazil.org/fuse/options_darwin.go @@ -0,0 +1,35 @@ +package fuse + +func localVolume(conf *mountConfig) error { + conf.options["local"] = "" + return nil +} + +func volumeName(name string) MountOption { + return func(conf *mountConfig) error { + conf.options["volname"] = name + return nil + } +} + +func daemonTimeout(name string) MountOption { + return func(conf *mountConfig) error { + conf.options["daemon_timeout"] = name + return nil + } +} + +func noAppleXattr(conf *mountConfig) error { + conf.options["noapplexattr"] = "" + return nil +} + +func noAppleDouble(conf *mountConfig) error { + conf.options["noappledouble"] = "" + return nil +} + +func exclCreate(conf *mountConfig) error { + conf.options["excl_create"] = "" + return nil +} diff --git a/vendor/bazil.org/fuse/options_freebsd.go b/vendor/bazil.org/fuse/options_freebsd.go new file mode 100644 index 000000000..7c164b136 --- /dev/null +++ b/vendor/bazil.org/fuse/options_freebsd.go @@ -0,0 +1,28 @@ +package fuse + +func localVolume(conf *mountConfig) error { + return nil +} + +func volumeName(name string) MountOption { + return dummyOption +} + +func daemonTimeout(name string) MountOption { + return func(conf *mountConfig) error { + conf.options["timeout"] = name + return nil + } +} + +func noAppleXattr(conf *mountConfig) error { + return nil +} + +func noAppleDouble(conf *mountConfig) error { + return nil +} + +func exclCreate(conf *mountConfig) error { + return nil +} diff --git a/vendor/bazil.org/fuse/options_linux.go b/vendor/bazil.org/fuse/options_linux.go new file mode 100644 index 000000000..13f0896d5 --- /dev/null +++ b/vendor/bazil.org/fuse/options_linux.go @@ -0,0 +1,25 @@ +package fuse + +func localVolume(conf *mountConfig) error { + return nil +} + +func volumeName(name string) MountOption { + return dummyOption +} + +func daemonTimeout(name string) MountOption { + return dummyOption +} + +func noAppleXattr(conf *mountConfig) error { + return nil +} + +func noAppleDouble(conf *mountConfig) error { + return nil +} + +func exclCreate(conf *mountConfig) error { + return nil +} diff --git a/vendor/bazil.org/fuse/protocol.go b/vendor/bazil.org/fuse/protocol.go new file mode 100644 index 000000000..a77bbf72f --- /dev/null +++ b/vendor/bazil.org/fuse/protocol.go @@ -0,0 +1,75 @@ +package fuse + +import ( + "fmt" +) + +// Protocol is a FUSE protocol version number. +type Protocol struct { + Major uint32 + Minor uint32 +} + +func (p Protocol) String() string { + return fmt.Sprintf("%d.%d", p.Major, p.Minor) +} + +// LT returns whether a is less than b. +func (a Protocol) LT(b Protocol) bool { + return a.Major < b.Major || + (a.Major == b.Major && a.Minor < b.Minor) +} + +// GE returns whether a is greater than or equal to b. +func (a Protocol) GE(b Protocol) bool { + return a.Major > b.Major || + (a.Major == b.Major && a.Minor >= b.Minor) +} + +func (a Protocol) is79() bool { + return a.GE(Protocol{7, 9}) +} + +// HasAttrBlockSize returns whether Attr.BlockSize is respected by the +// kernel. +func (a Protocol) HasAttrBlockSize() bool { + return a.is79() +} + +// HasReadWriteFlags returns whether ReadRequest/WriteRequest +// fields Flags and FileFlags are valid. +func (a Protocol) HasReadWriteFlags() bool { + return a.is79() +} + +// HasGetattrFlags returns whether GetattrRequest field Flags is +// valid. +func (a Protocol) HasGetattrFlags() bool { + return a.is79() +} + +func (a Protocol) is710() bool { + return a.GE(Protocol{7, 10}) +} + +// HasOpenNonSeekable returns whether OpenResponse field Flags flag +// OpenNonSeekable is supported. +func (a Protocol) HasOpenNonSeekable() bool { + return a.is710() +} + +func (a Protocol) is712() bool { + return a.GE(Protocol{7, 12}) +} + +// HasUmask returns whether CreateRequest/MkdirRequest/MknodRequest +// field Umask is valid. +func (a Protocol) HasUmask() bool { + return a.is712() +} + +// HasInvalidate returns whether InvalidateNode/InvalidateEntry are +// supported. +func (a Protocol) HasInvalidate() bool { + return a.is712() +} diff --git a/vendor/bazil.org/fuse/unmount.go b/vendor/bazil.org/fuse/unmount.go new file mode 100644 index 000000000..ffe3f155c --- /dev/null +++ b/vendor/bazil.org/fuse/unmount.go @@ -0,0 +1,6 @@ +package fuse + +// Unmount tries to unmount the filesystem mounted at dir. +func Unmount(dir string) error { + return unmount(dir) +} diff --git a/vendor/bazil.org/fuse/unmount_linux.go b/vendor/bazil.org/fuse/unmount_linux.go new file mode 100644 index 000000000..088f0cfee --- /dev/null +++ b/vendor/bazil.org/fuse/unmount_linux.go @@ -0,0 +1,21 @@ +package fuse + +import ( + "bytes" + "errors" + "os/exec" +) + +func unmount(dir string) error { + cmd := exec.Command("fusermount", "-u", dir) + output, err := cmd.CombinedOutput() + if err != nil { + if len(output) > 0 { + output = bytes.TrimRight(output, "\n") + msg := err.Error() + ": " + string(output) + err = errors.New(msg) + } + return err + } + return nil +} diff --git a/vendor/bazil.org/fuse/unmount_std.go b/vendor/bazil.org/fuse/unmount_std.go new file mode 100644 index 000000000..d6efe276f --- /dev/null +++ b/vendor/bazil.org/fuse/unmount_std.go @@ -0,0 +1,17 @@ +// +build !linux + +package fuse + +import ( + "os" + "syscall" +) + +func unmount(dir string) error { + err := syscall.Unmount(dir, 0) + if err != nil { + err = &os.PathError{Op: "unmount", Path: dir, Err: err} + return err + } + return nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/README.md b/vendor/github.com/Azure/azure-sdk-for-go/storage/README.md deleted file mode 100644 index 0ab099848..000000000 --- a/vendor/github.com/Azure/azure-sdk-for-go/storage/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Azure Storage SDK for Go - -The `github.com/Azure/azure-sdk-for-go/storage` package is used to perform operations in Azure Storage Service. To manage your storage accounts (Azure Resource Manager / ARM), use the [github.com/Azure/azure-sdk-for-go/arm/storage](../arm/storage) package. For your classic storage accounts (Azure Service Management / ASM), use [github.com/Azure/azure-sdk-for-go/management/storageservice](../management/storageservice) package. - -This package includes support for [Azure Storage Emulator](https://azure.microsoft.com/documentation/articles/storage-use-emulator/)
\ No newline at end of file diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go deleted file mode 100644 index 2397587c8..000000000 --- a/vendor/github.com/Azure/azure-sdk-for-go/storage/file.go +++ /dev/null @@ -1,352 +0,0 @@ -package storage - -import ( - "encoding/xml" - "fmt" - "net/http" - "net/url" - "strings" -) - -// FileServiceClient contains operations for Microsoft Azure File Service. -type FileServiceClient struct { - client Client -} - -// A Share is an entry in ShareListResponse. -type Share struct { - Name string `xml:"Name"` - Properties ShareProperties `xml:"Properties"` -} - -// ShareProperties contains various properties of a share returned from -// various endpoints like ListShares. -type ShareProperties struct { - LastModified string `xml:"Last-Modified"` - Etag string `xml:"Etag"` - Quota string `xml:"Quota"` -} - -// ShareListResponse contains the response fields from -// ListShares call. -// -// See https://msdn.microsoft.com/en-us/library/azure/dn167009.aspx -type ShareListResponse struct { - XMLName xml.Name `xml:"EnumerationResults"` - Xmlns string `xml:"xmlns,attr"` - Prefix string `xml:"Prefix"` - Marker string `xml:"Marker"` - NextMarker string `xml:"NextMarker"` - MaxResults int64 `xml:"MaxResults"` - Shares []Share `xml:"Shares>Share"` -} - -// ListSharesParameters defines the set of customizable parameters to make a -// List Shares call. -// -// See https://msdn.microsoft.com/en-us/library/azure/dn167009.aspx -type ListSharesParameters struct { - Prefix string - Marker string - Include string - MaxResults uint - Timeout uint -} - -// ShareHeaders contains various properties of a file and is an entry -// in SetShareProperties -type ShareHeaders struct { - Quota string `header:"x-ms-share-quota"` -} - -func (p ListSharesParameters) getParameters() url.Values { - out := url.Values{} - - if p.Prefix != "" { - out.Set("prefix", p.Prefix) - } - if p.Marker != "" { - out.Set("marker", p.Marker) - } - if p.Include != "" { - out.Set("include", p.Include) - } - if p.MaxResults != 0 { - out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) - } - if p.Timeout != 0 { - out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) - } - - return out -} - -// pathForFileShare returns the URL path segment for a File Share resource -func pathForFileShare(name string) string { - return fmt.Sprintf("/%s", name) -} - -// ListShares returns the list of shares in a storage account along with -// pagination token and other response details. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx -func (f FileServiceClient) ListShares(params ListSharesParameters) (ShareListResponse, error) { - q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}}) - uri := f.client.getEndpoint(fileServiceName, "", q) - headers := f.client.getStandardHeaders() - - var out ShareListResponse - resp, err := f.client.exec("GET", uri, headers, nil) - if err != nil { - return out, err - } - defer resp.body.Close() - - err = xmlUnmarshal(resp.body, &out) - return out, err -} - -// CreateShare operation creates a new share under the specified account. If the -// share with the same name already exists, the operation fails. -// -// See https://msdn.microsoft.com/en-us/library/azure/dn167008.aspx -func (f FileServiceClient) CreateShare(name string) error { - resp, err := f.createShare(name) - if err != nil { - return err - } - defer resp.body.Close() - return checkRespCode(resp.statusCode, []int{http.StatusCreated}) -} - -// ShareExists returns true if a share with given name exists -// on the storage account, otherwise returns false. -func (f FileServiceClient) ShareExists(name string) (bool, error) { - uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{"restype": {"share"}}) - headers := f.client.getStandardHeaders() - - resp, err := f.client.exec("HEAD", uri, headers, nil) - if resp != nil { - defer resp.body.Close() - if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound { - return resp.statusCode == http.StatusOK, nil - } - } - return false, err -} - -// GetShareURL gets the canonical URL to the share with the specified name in the -// specified container. This method does not create a publicly accessible URL if -// the file is private and this method does not check if the file -// exists. -func (f FileServiceClient) GetShareURL(name string) string { - return f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{}) -} - -// CreateShareIfNotExists creates a new share under the specified account if -// it does not exist. Returns true if container is newly created or false if -// container already exists. -// -// See https://msdn.microsoft.com/en-us/library/azure/dn167008.aspx -func (f FileServiceClient) CreateShareIfNotExists(name string) (bool, error) { - resp, err := f.createShare(name) - if resp != nil { - defer resp.body.Close() - if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict { - return resp.statusCode == http.StatusCreated, nil - } - } - return false, err -} - -// CreateShare creates a Azure File Share and returns its response -func (f FileServiceClient) createShare(name string) (*storageResponse, error) { - if err := f.checkForStorageEmulator(); err != nil { - return nil, err - } - uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{"restype": {"share"}}) - headers := f.client.getStandardHeaders() - return f.client.exec("PUT", uri, headers, nil) -} - -// GetShareProperties provides various information about the specified -// file. See https://msdn.microsoft.com/en-us/library/azure/dn689099.aspx -func (f FileServiceClient) GetShareProperties(name string) (*ShareProperties, error) { - uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{"restype": {"share"}}) - - headers := f.client.getStandardHeaders() - resp, err := f.client.exec("HEAD", uri, headers, nil) - if err != nil { - return nil, err - } - defer resp.body.Close() - - if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { - return nil, err - } - - return &ShareProperties{ - LastModified: resp.headers.Get("Last-Modified"), - Etag: resp.headers.Get("Etag"), - Quota: resp.headers.Get("x-ms-share-quota"), - }, nil -} - -// SetShareProperties replaces the ShareHeaders for the specified file. -// -// Some keys may be converted to Camel-Case before sending. All keys -// are returned in lower case by SetShareProperties. HTTP header names -// are case-insensitive so case munging should not matter to other -// applications either. -// -// See https://msdn.microsoft.com/en-us/library/azure/mt427368.aspx -func (f FileServiceClient) SetShareProperties(name string, shareHeaders ShareHeaders) error { - params := url.Values{} - params.Set("restype", "share") - params.Set("comp", "properties") - - uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), params) - headers := f.client.getStandardHeaders() - - extraHeaders := headersFromStruct(shareHeaders) - - for k, v := range extraHeaders { - headers[k] = v - } - - resp, err := f.client.exec("PUT", uri, headers, nil) - if err != nil { - return err - } - defer resp.body.Close() - - return checkRespCode(resp.statusCode, []int{http.StatusOK}) -} - -// DeleteShare operation marks the specified share for deletion. The share -// and any files contained within it are later deleted during garbage -// collection. -// -// See https://msdn.microsoft.com/en-us/library/azure/dn689090.aspx -func (f FileServiceClient) DeleteShare(name string) error { - resp, err := f.deleteShare(name) - if err != nil { - return err - } - defer resp.body.Close() - return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) -} - -// DeleteShareIfExists operation marks the specified share for deletion if it -// exists. The share and any files contained within it are later deleted during -// garbage collection. Returns true if share existed and deleted with this call, -// false otherwise. -// -// See https://msdn.microsoft.com/en-us/library/azure/dn689090.aspx -func (f FileServiceClient) DeleteShareIfExists(name string) (bool, error) { - resp, err := f.deleteShare(name) - if resp != nil { - defer resp.body.Close() - if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { - return resp.statusCode == http.StatusAccepted, nil - } - } - return false, err -} - -// deleteShare makes the call to Delete Share operation endpoint and returns -// the response -func (f FileServiceClient) deleteShare(name string) (*storageResponse, error) { - if err := f.checkForStorageEmulator(); err != nil { - return nil, err - } - uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), url.Values{"restype": {"share"}}) - return f.client.exec("DELETE", uri, f.client.getStandardHeaders(), nil) -} - -// SetShareMetadata replaces the metadata for the specified Share. -// -// Some keys may be converted to Camel-Case before sending. All keys -// are returned in lower case by GetShareMetadata. HTTP header names -// are case-insensitive so case munging should not matter to other -// applications either. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx -func (f FileServiceClient) SetShareMetadata(name string, metadata map[string]string, extraHeaders map[string]string) error { - params := url.Values{} - params.Set("restype", "share") - params.Set("comp", "metadata") - - uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), params) - headers := f.client.getStandardHeaders() - for k, v := range metadata { - headers[userDefinedMetadataHeaderPrefix+k] = v - } - - for k, v := range extraHeaders { - headers[k] = v - } - - resp, err := f.client.exec("PUT", uri, headers, nil) - if err != nil { - return err - } - defer resp.body.Close() - - return checkRespCode(resp.statusCode, []int{http.StatusOK}) -} - -// GetShareMetadata returns all user-defined metadata for the specified share. -// -// All metadata keys will be returned in lower case. (HTTP header -// names are case-insensitive.) -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx -func (f FileServiceClient) GetShareMetadata(name string) (map[string]string, error) { - params := url.Values{} - params.Set("restype", "share") - params.Set("comp", "metadata") - - uri := f.client.getEndpoint(fileServiceName, pathForFileShare(name), params) - headers := f.client.getStandardHeaders() - - resp, err := f.client.exec("GET", uri, headers, nil) - if err != nil { - return nil, err - } - defer resp.body.Close() - - if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { - return nil, err - } - - metadata := make(map[string]string) - for k, v := range resp.headers { - // Can't trust CanonicalHeaderKey() to munge case - // reliably. "_" is allowed in identifiers: - // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx - // https://msdn.microsoft.com/library/aa664670(VS.71).aspx - // http://tools.ietf.org/html/rfc7230#section-3.2 - // ...but "_" is considered invalid by - // CanonicalMIMEHeaderKey in - // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542 - // so k can be "X-Ms-Meta-Foo" or "x-ms-meta-foo_bar". - k = strings.ToLower(k) - if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) { - continue - } - // metadata["foo"] = content of the last X-Ms-Meta-Foo header - k = k[len(userDefinedMetadataHeaderPrefix):] - metadata[k] = v[len(v)-1] - } - return metadata, nil -} - -//checkForStorageEmulator determines if the client is setup for use with -//Azure Storage Emulator, and returns a relevant error -func (f FileServiceClient) checkForStorageEmulator() error { - if f.client.accountName == StorageEmulatorAccountName { - return fmt.Errorf("Error: File service is not currently supported by Azure Storage Emulator") - } - return nil -} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go b/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go deleted file mode 100644 index 39e997503..000000000 --- a/vendor/github.com/Azure/azure-sdk-for-go/storage/table.go +++ /dev/null @@ -1,129 +0,0 @@ -package storage - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/url" -) - -// TableServiceClient contains operations for Microsoft Azure Table Storage -// Service. -type TableServiceClient struct { - client Client -} - -// AzureTable is the typedef of the Azure Table name -type AzureTable string - -const ( - tablesURIPath = "/Tables" -) - -type createTableRequest struct { - TableName string `json:"TableName"` -} - -func pathForTable(table AzureTable) string { return fmt.Sprintf("%s", table) } - -func (c *TableServiceClient) getStandardHeaders() map[string]string { - return map[string]string{ - "x-ms-version": "2015-02-21", - "x-ms-date": currentTimeRfc1123Formatted(), - "Accept": "application/json;odata=nometadata", - "Accept-Charset": "UTF-8", - "Content-Type": "application/json", - } -} - -// QueryTables returns the tables created in the -// *TableServiceClient storage account. -func (c *TableServiceClient) QueryTables() ([]AzureTable, error) { - uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) - - headers := c.getStandardHeaders() - headers["Content-Length"] = "0" - - resp, err := c.client.execTable("GET", uri, headers, nil) - if err != nil { - return nil, err - } - defer resp.body.Close() - - if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { - return nil, err - } - - buf := new(bytes.Buffer) - buf.ReadFrom(resp.body) - - var respArray queryTablesResponse - if err := json.Unmarshal(buf.Bytes(), &respArray); err != nil { - return nil, err - } - - s := make([]AzureTable, len(respArray.TableName)) - for i, elem := range respArray.TableName { - s[i] = AzureTable(elem.TableName) - } - - return s, nil -} - -// CreateTable creates the table given the specific -// name. This function fails if the name is not compliant -// with the specification or the tables already exists. -func (c *TableServiceClient) CreateTable(table AzureTable) error { - uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) - - headers := c.getStandardHeaders() - - req := createTableRequest{TableName: string(table)} - buf := new(bytes.Buffer) - - if err := json.NewEncoder(buf).Encode(req); err != nil { - return err - } - - headers["Content-Length"] = fmt.Sprintf("%d", buf.Len()) - - resp, err := c.client.execTable("POST", uri, headers, buf) - - if err != nil { - return err - } - defer resp.body.Close() - - if err := checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil { - return err - } - - return nil -} - -// DeleteTable deletes the table given the specific -// name. This function fails if the table is not present. -// Be advised: DeleteTable deletes all the entries -// that may be present. -func (c *TableServiceClient) DeleteTable(table AzureTable) error { - uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) - uri += fmt.Sprintf("('%s')", string(table)) - - headers := c.getStandardHeaders() - - headers["Content-Length"] = "0" - - resp, err := c.client.execTable("DELETE", uri, headers, nil) - - if err != nil { - return err - } - defer resp.body.Close() - - if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil { - return err - - } - return nil -} diff --git a/vendor/github.com/Azure/azure-storage-go/LICENSE b/vendor/github.com/Azure/azure-storage-go/LICENSE new file mode 100644 index 000000000..21071075c --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/vendor/github.com/Azure/azure-storage-go/README.md b/vendor/github.com/Azure/azure-storage-go/README.md new file mode 100644 index 000000000..f4af7bf39 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/README.md @@ -0,0 +1,10 @@ +# Azure Storage SDK for Go +[](https://godoc.org/github.com/Azure/azure-storage-go) [](https://travis-ci.org/Azure/azure-storage-go) [](https://goreportcard.com/report/github.com/Azure/azure-storage-go) + +The `github.com/Azure/azure-sdk-for-go/storage` package is used to perform operations in Azure Storage Service. To manage your storage accounts (Azure Resource Manager / ARM), use the [github.com/Azure/azure-sdk-for-go/arm/storage](../arm/storage) package. For your classic storage accounts (Azure Service Management / ASM), use [github.com/Azure/azure-sdk-for-go/management/storageservice](../management/storageservice) package. + +This package includes support for [Azure Storage Emulator](https://azure.microsoft.com/documentation/articles/storage-use-emulator/) + +# Contributing + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/vendor/github.com/Azure/azure-storage-go/authorization.go b/vendor/github.com/Azure/azure-storage-go/authorization.go new file mode 100644 index 000000000..89a0d0b3c --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/authorization.go @@ -0,0 +1,223 @@ +// Package storage provides clients for Microsoft Azure Storage Services. +package storage + +import ( + "bytes" + "fmt" + "net/url" + "sort" + "strings" +) + +// See: https://docs.microsoft.com/rest/api/storageservices/fileservices/authentication-for-the-azure-storage-services + +type authentication string + +const ( + sharedKey authentication = "sharedKey" + sharedKeyForTable authentication = "sharedKeyTable" + sharedKeyLite authentication = "sharedKeyLite" + sharedKeyLiteForTable authentication = "sharedKeyLiteTable" + + // headers + headerAuthorization = "Authorization" + headerContentLength = "Content-Length" + headerDate = "Date" + headerXmsDate = "x-ms-date" + headerXmsVersion = "x-ms-version" + headerContentEncoding = "Content-Encoding" + headerContentLanguage = "Content-Language" + headerContentType = "Content-Type" + headerContentMD5 = "Content-MD5" + headerIfModifiedSince = "If-Modified-Since" + headerIfMatch = "If-Match" + headerIfNoneMatch = "If-None-Match" + headerIfUnmodifiedSince = "If-Unmodified-Since" + headerRange = "Range" +) + +func (c *Client) addAuthorizationHeader(verb, url string, headers map[string]string, auth authentication) (map[string]string, error) { + authHeader, err := c.getSharedKey(verb, url, headers, auth) + if err != nil { + return nil, err + } + headers[headerAuthorization] = authHeader + return headers, nil +} + +func (c *Client) getSharedKey(verb, url string, headers map[string]string, auth authentication) (string, error) { + canRes, err := c.buildCanonicalizedResource(url, auth) + if err != nil { + return "", err + } + + canString, err := buildCanonicalizedString(verb, headers, canRes, auth) + if err != nil { + return "", err + } + return c.createAuthorizationHeader(canString, auth), nil +} + +func (c *Client) buildCanonicalizedResource(uri string, auth authentication) (string, error) { + errMsg := "buildCanonicalizedResource error: %s" + u, err := url.Parse(uri) + if err != nil { + return "", fmt.Errorf(errMsg, err.Error()) + } + + cr := bytes.NewBufferString("/") + cr.WriteString(c.getCanonicalizedAccountName()) + + if len(u.Path) > 0 { + // Any portion of the CanonicalizedResource string that is derived from + // the resource's URI should be encoded exactly as it is in the URI. + // -- https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx + cr.WriteString(u.EscapedPath()) + } + + params, err := url.ParseQuery(u.RawQuery) + if err != nil { + return "", fmt.Errorf(errMsg, err.Error()) + } + + // See https://github.com/Azure/azure-storage-net/blob/master/Lib/Common/Core/Util/AuthenticationUtility.cs#L277 + if auth == sharedKey { + if len(params) > 0 { + cr.WriteString("\n") + + keys := []string{} + for key := range params { + keys = append(keys, key) + } + sort.Strings(keys) + + completeParams := []string{} + for _, key := range keys { + if len(params[key]) > 1 { + sort.Strings(params[key]) + } + + completeParams = append(completeParams, fmt.Sprintf("%s:%s", key, strings.Join(params[key], ","))) + } + cr.WriteString(strings.Join(completeParams, "\n")) + } + } else { + // search for "comp" parameter, if exists then add it to canonicalizedresource + if v, ok := params["comp"]; ok { + cr.WriteString("?comp=" + v[0]) + } + } + + return string(cr.Bytes()), nil +} + +func (c *Client) getCanonicalizedAccountName() string { + // since we may be trying to access a secondary storage account, we need to + // remove the -secondary part of the storage name + return strings.TrimSuffix(c.accountName, "-secondary") +} + +func buildCanonicalizedString(verb string, headers map[string]string, canonicalizedResource string, auth authentication) (string, error) { + contentLength := headers[headerContentLength] + if contentLength == "0" { + contentLength = "" + } + date := headers[headerDate] + if v, ok := headers[headerXmsDate]; ok { + if auth == sharedKey || auth == sharedKeyLite { + date = "" + } else { + date = v + } + } + var canString string + switch auth { + case sharedKey: + canString = strings.Join([]string{ + verb, + headers[headerContentEncoding], + headers[headerContentLanguage], + contentLength, + headers[headerContentMD5], + headers[headerContentType], + date, + headers[headerIfModifiedSince], + headers[headerIfMatch], + headers[headerIfNoneMatch], + headers[headerIfUnmodifiedSince], + headers[headerRange], + buildCanonicalizedHeader(headers), + canonicalizedResource, + }, "\n") + case sharedKeyForTable: + canString = strings.Join([]string{ + verb, + headers[headerContentMD5], + headers[headerContentType], + date, + canonicalizedResource, + }, "\n") + case sharedKeyLite: + canString = strings.Join([]string{ + verb, + headers[headerContentMD5], + headers[headerContentType], + date, + buildCanonicalizedHeader(headers), + canonicalizedResource, + }, "\n") + case sharedKeyLiteForTable: + canString = strings.Join([]string{ + date, + canonicalizedResource, + }, "\n") + default: + return "", fmt.Errorf("%s authentication is not supported yet", auth) + } + return canString, nil +} + +func buildCanonicalizedHeader(headers map[string]string) string { + cm := make(map[string]string) + + for k, v := range headers { + headerName := strings.TrimSpace(strings.ToLower(k)) + if strings.HasPrefix(headerName, "x-ms-") { + cm[headerName] = v + } + } + + if len(cm) == 0 { + return "" + } + + keys := []string{} + for key := range cm { + keys = append(keys, key) + } + + sort.Strings(keys) + + ch := bytes.NewBufferString("") + + for _, key := range keys { + ch.WriteString(key) + ch.WriteRune(':') + ch.WriteString(cm[key]) + ch.WriteRune('\n') + } + + return strings.TrimSuffix(string(ch.Bytes()), "\n") +} + +func (c *Client) createAuthorizationHeader(canonicalizedString string, auth authentication) string { + signature := c.computeHmac256(canonicalizedString) + var key string + switch auth { + case sharedKey, sharedKeyForTable: + key = "SharedKey" + case sharedKeyLite, sharedKeyLiteForTable: + key = "SharedKeyLite" + } + return fmt.Sprintf("%s %s:%s", key, c.getCanonicalizedAccountName(), signature) +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go b/vendor/github.com/Azure/azure-storage-go/blob.go index 317620363..636efc662 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/storage/blob.go +++ b/vendor/github.com/Azure/azure-storage-go/blob.go @@ -13,44 +13,6 @@ import ( "time" ) -// BlobStorageClient contains operations for Microsoft Azure Blob Storage -// Service. -type BlobStorageClient struct { - client Client -} - -// A Container is an entry in ContainerListResponse. -type Container struct { - Name string `xml:"Name"` - Properties ContainerProperties `xml:"Properties"` - // TODO (ahmetalpbalkan) Metadata -} - -// ContainerProperties contains various properties of a container returned from -// various endpoints like ListContainers. -type ContainerProperties struct { - LastModified string `xml:"Last-Modified"` - Etag string `xml:"Etag"` - LeaseStatus string `xml:"LeaseStatus"` - LeaseState string `xml:"LeaseState"` - LeaseDuration string `xml:"LeaseDuration"` - // TODO (ahmetalpbalkan) remaining fields -} - -// ContainerListResponse contains the response fields from -// ListContainers call. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx -type ContainerListResponse struct { - XMLName xml.Name `xml:"EnumerationResults"` - Xmlns string `xml:"xmlns,attr"` - Prefix string `xml:"Prefix"` - Marker string `xml:"Marker"` - NextMarker string `xml:"NextMarker"` - MaxResults int64 `xml:"MaxResults"` - Containers []Container `xml:"Containers>Container"` -} - // A Blob is an entry in BlobListResponse. type Blob struct { Name string `xml:"Name"` @@ -122,6 +84,7 @@ type BlobProperties struct { CopyCompletionTime string `xml:"CopyCompletionTime"` CopyStatusDescription string `xml:"CopyStatusDescription"` LeaseStatus string `xml:"LeaseStatus"` + LeaseState string `xml:"LeaseState"` } // BlobHeaders contains various properties of a blob and is an entry @@ -134,101 +97,6 @@ type BlobHeaders struct { CacheControl string `header:"x-ms-blob-cache-control"` } -// BlobListResponse contains the response fields from ListBlobs call. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx -type BlobListResponse struct { - XMLName xml.Name `xml:"EnumerationResults"` - Xmlns string `xml:"xmlns,attr"` - Prefix string `xml:"Prefix"` - Marker string `xml:"Marker"` - NextMarker string `xml:"NextMarker"` - MaxResults int64 `xml:"MaxResults"` - Blobs []Blob `xml:"Blobs>Blob"` - - // BlobPrefix is used to traverse blobs as if it were a file system. - // It is returned if ListBlobsParameters.Delimiter is specified. - // The list here can be thought of as "folders" that may contain - // other folders or blobs. - BlobPrefixes []string `xml:"Blobs>BlobPrefix>Name"` - - // Delimiter is used to traverse blobs as if it were a file system. - // It is returned if ListBlobsParameters.Delimiter is specified. - Delimiter string `xml:"Delimiter"` -} - -// ListContainersParameters defines the set of customizable parameters to make a -// List Containers call. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx -type ListContainersParameters struct { - Prefix string - Marker string - Include string - MaxResults uint - Timeout uint -} - -func (p ListContainersParameters) getParameters() url.Values { - out := url.Values{} - - if p.Prefix != "" { - out.Set("prefix", p.Prefix) - } - if p.Marker != "" { - out.Set("marker", p.Marker) - } - if p.Include != "" { - out.Set("include", p.Include) - } - if p.MaxResults != 0 { - out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) - } - if p.Timeout != 0 { - out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) - } - - return out -} - -// ListBlobsParameters defines the set of customizable -// parameters to make a List Blobs call. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx -type ListBlobsParameters struct { - Prefix string - Delimiter string - Marker string - Include string - MaxResults uint - Timeout uint -} - -func (p ListBlobsParameters) getParameters() url.Values { - out := url.Values{} - - if p.Prefix != "" { - out.Set("prefix", p.Prefix) - } - if p.Delimiter != "" { - out.Set("delimiter", p.Delimiter) - } - if p.Marker != "" { - out.Set("marker", p.Marker) - } - if p.Include != "" { - out.Set("include", p.Include) - } - if p.MaxResults != 0 { - out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) - } - if p.Timeout != 0 { - out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) - } - - return out -} - // BlobType defines the type of the Azure Blob. type BlobType string @@ -259,7 +127,7 @@ const ( // lease constants. const ( leaseHeaderPrefix = "x-ms-lease-" - leaseID = "x-ms-lease-id" + headerLeaseID = "x-ms-lease-id" leaseAction = "x-ms-lease-action" leaseBreakPeriod = "x-ms-lease-break-period" leaseDuration = "x-ms-lease-duration" @@ -287,23 +155,9 @@ const ( BlockListTypeUncommitted BlockListType = "uncommitted" ) -// ContainerAccessType defines the access level to the container from a public -// request. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179468.aspx and "x-ms- -// blob-public-access" header. -type ContainerAccessType string - -// Access options for containers -const ( - ContainerAccessTypePrivate ContainerAccessType = "" - ContainerAccessTypeBlob ContainerAccessType = "blob" - ContainerAccessTypeContainer ContainerAccessType = "container" -) - // Maximum sizes (per REST API) for various concepts const ( - MaxBlobBlockSize = 4 * 1024 * 1024 + MaxBlobBlockSize = 100 * 1024 * 1024 MaxBlobPageSize = 4 * 1024 * 1024 ) @@ -341,7 +195,7 @@ type BlockResponse struct { Size int64 `xml:"Size"` } -// GetPageRangesResponse contains the reponse fields from +// GetPageRangesResponse contains the response fields from // Get Page Ranges call. // // See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx @@ -364,149 +218,14 @@ var ( errBlobCopyIDMismatch = errors.New("storage: blob copy id is a mismatch") ) -// ListContainers returns the list of containers in a storage account along with -// pagination token and other response details. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx -func (b BlobStorageClient) ListContainers(params ListContainersParameters) (ContainerListResponse, error) { - q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}}) - uri := b.client.getEndpoint(blobServiceName, "", q) - headers := b.client.getStandardHeaders() - - var out ContainerListResponse - resp, err := b.client.exec("GET", uri, headers, nil) - if err != nil { - return out, err - } - defer resp.body.Close() - - err = xmlUnmarshal(resp.body, &out) - return out, err -} - -// CreateContainer creates a blob container within the storage account -// with given name and access level. Returns error if container already exists. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179468.aspx -func (b BlobStorageClient) CreateContainer(name string, access ContainerAccessType) error { - resp, err := b.createContainer(name, access) - if err != nil { - return err - } - defer resp.body.Close() - return checkRespCode(resp.statusCode, []int{http.StatusCreated}) -} - -// CreateContainerIfNotExists creates a blob container if it does not exist. Returns -// true if container is newly created or false if container already exists. -func (b BlobStorageClient) CreateContainerIfNotExists(name string, access ContainerAccessType) (bool, error) { - resp, err := b.createContainer(name, access) - if resp != nil { - defer resp.body.Close() - if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict { - return resp.statusCode == http.StatusCreated, nil - } - } - return false, err -} - -func (b BlobStorageClient) createContainer(name string, access ContainerAccessType) (*storageResponse, error) { - verb := "PUT" - uri := b.client.getEndpoint(blobServiceName, pathForContainer(name), url.Values{"restype": {"container"}}) - - headers := b.client.getStandardHeaders() - if access != "" { - headers["x-ms-blob-public-access"] = string(access) - } - return b.client.exec(verb, uri, headers, nil) -} - -// ContainerExists returns true if a container with given name exists -// on the storage account, otherwise returns false. -func (b BlobStorageClient) ContainerExists(name string) (bool, error) { - verb := "HEAD" - uri := b.client.getEndpoint(blobServiceName, pathForContainer(name), url.Values{"restype": {"container"}}) - headers := b.client.getStandardHeaders() - - resp, err := b.client.exec(verb, uri, headers, nil) - if resp != nil { - defer resp.body.Close() - if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound { - return resp.statusCode == http.StatusOK, nil - } - } - return false, err -} - -// DeleteContainer deletes the container with given name on the storage -// account. If the container does not exist returns error. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179408.aspx -func (b BlobStorageClient) DeleteContainer(name string) error { - resp, err := b.deleteContainer(name) - if err != nil { - return err - } - defer resp.body.Close() - return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) -} - -// DeleteContainerIfExists deletes the container with given name on the storage -// account if it exists. Returns true if container is deleted with this call, or -// false if the container did not exist at the time of the Delete Container -// operation. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd179408.aspx -func (b BlobStorageClient) DeleteContainerIfExists(name string) (bool, error) { - resp, err := b.deleteContainer(name) - if resp != nil { - defer resp.body.Close() - if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { - return resp.statusCode == http.StatusAccepted, nil - } - } - return false, err -} - -func (b BlobStorageClient) deleteContainer(name string) (*storageResponse, error) { - verb := "DELETE" - uri := b.client.getEndpoint(blobServiceName, pathForContainer(name), url.Values{"restype": {"container"}}) - - headers := b.client.getStandardHeaders() - return b.client.exec(verb, uri, headers, nil) -} - -// ListBlobs returns an object that contains list of blobs in the container, -// pagination token and other information in the response of List Blobs call. -// -// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx -func (b BlobStorageClient) ListBlobs(container string, params ListBlobsParameters) (BlobListResponse, error) { - q := mergeParams(params.getParameters(), url.Values{ - "restype": {"container"}, - "comp": {"list"}}) - uri := b.client.getEndpoint(blobServiceName, pathForContainer(container), q) - headers := b.client.getStandardHeaders() - - var out BlobListResponse - resp, err := b.client.exec("GET", uri, headers, nil) - if err != nil { - return out, err - } - defer resp.body.Close() - - err = xmlUnmarshal(resp.body, &out) - return out, err -} - // BlobExists returns true if a blob with given name exists on the specified // container of the storage account. func (b BlobStorageClient) BlobExists(container, name string) (bool, error) { - verb := "HEAD" uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) headers := b.client.getStandardHeaders() - resp, err := b.client.exec(verb, uri, headers, nil) + resp, err := b.client.exec(http.MethodHead, uri, headers, nil, b.auth) if resp != nil { - defer resp.body.Close() + defer readAndCloseBody(resp.body) if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound { return resp.statusCode == http.StatusOK, nil } @@ -515,14 +234,15 @@ func (b BlobStorageClient) BlobExists(container, name string) (bool, error) { } // GetBlobURL gets the canonical URL to the blob with the specified name in the -// specified container. This method does not create a publicly accessible URL if -// the blob or container is private and this method does not check if the blob -// exists. +// specified container. If name is not specified, the canonical URL for the entire +// container is obtained. +// This method does not create a publicly accessible URL if the blob or container +// is private and this method does not check if the blob exists. func (b BlobStorageClient) GetBlobURL(container, name string) string { if container == "" { container = "$root" } - return b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) + return b.client.getEndpoint(blobServiceName, pathForResource(container, name), url.Values{}) } // GetBlob returns a stream to read the blob. Caller must call Close() the @@ -558,9 +278,9 @@ func (b BlobStorageClient) GetBlobRange(container, name, bytesRange string, extr } func (b BlobStorageClient) getBlobRange(container, name, bytesRange string, extraHeaders map[string]string) (*storageResponse, error) { - verb := "GET" uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() if bytesRange != "" { headers["Range"] = fmt.Sprintf("bytes=%s", bytesRange) @@ -570,23 +290,23 @@ func (b BlobStorageClient) getBlobRange(container, name, bytesRange string, extr headers[k] = v } - resp, err := b.client.exec(verb, uri, headers, nil) + resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth) if err != nil { return nil, err } return resp, err } -// leasePut is common PUT code for the various aquire/release/break etc functions. +// leasePut is common PUT code for the various acquire/release/break etc functions. func (b BlobStorageClient) leaseCommonPut(container string, name string, headers map[string]string, expectedStatus int) (http.Header, error) { params := url.Values{"comp": {"lease"}} uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) - resp, err := b.client.exec("PUT", uri, headers, nil) + resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth) if err != nil { return nil, err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) if err := checkRespCode(resp.statusCode, []int{expectedStatus}); err != nil { return nil, err @@ -595,27 +315,78 @@ func (b BlobStorageClient) leaseCommonPut(container string, name string, headers return resp.headers, nil } +// SnapshotBlob creates a snapshot for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691971.aspx +func (b BlobStorageClient) SnapshotBlob(container string, name string, timeout int, extraHeaders map[string]string) (snapshotTimestamp *time.Time, err error) { + extraHeaders = b.client.protectUserAgent(extraHeaders) + headers := b.client.getStandardHeaders() + params := url.Values{"comp": {"snapshot"}} + + if timeout > 0 { + params.Add("timeout", strconv.Itoa(timeout)) + } + + for k, v := range extraHeaders { + headers[k] = v + } + + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) + resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth) + if err != nil || resp == nil { + return nil, err + } + + defer readAndCloseBody(resp.body) + + if err := checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil { + return nil, err + } + + snapshotResponse := resp.headers.Get(http.CanonicalHeaderKey("x-ms-snapshot")) + if snapshotResponse != "" { + snapshotTimestamp, err := time.Parse(time.RFC3339, snapshotResponse) + if err != nil { + return nil, err + } + + return &snapshotTimestamp, nil + } + + return nil, errors.New("Snapshot not created") +} + // AcquireLease creates a lease for a blob as per https://msdn.microsoft.com/en-us/library/azure/ee691972.aspx // returns leaseID acquired +// In API Versions starting on 2012-02-12, the minimum leaseTimeInSeconds is 15, the maximum +// non-infinite leaseTimeInSeconds is 60. To specify an infinite lease, provide the value -1. func (b BlobStorageClient) AcquireLease(container string, name string, leaseTimeInSeconds int, proposedLeaseID string) (returnedLeaseID string, err error) { headers := b.client.getStandardHeaders() headers[leaseAction] = acquireLease - headers[leaseProposedID] = proposedLeaseID + + if leaseTimeInSeconds == -1 { + // Do nothing, but don't trigger the following clauses. + } else if leaseTimeInSeconds > 60 || b.client.apiVersion < "2012-02-12" { + leaseTimeInSeconds = 60 + } else if leaseTimeInSeconds < 15 { + leaseTimeInSeconds = 15 + } + headers[leaseDuration] = strconv.Itoa(leaseTimeInSeconds) + if proposedLeaseID != "" { + headers[leaseProposedID] = proposedLeaseID + } + respHeaders, err := b.leaseCommonPut(container, name, headers, http.StatusCreated) if err != nil { return "", err } - returnedLeaseID = respHeaders.Get(http.CanonicalHeaderKey(leaseID)) + returnedLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID)) if returnedLeaseID != "" { return returnedLeaseID, nil } - // what should we return in case of HTTP 201 but no lease ID? - // or it just cant happen? (brave words) return "", errors.New("LeaseID not returned") } @@ -661,7 +432,7 @@ func (b BlobStorageClient) breakLeaseCommon(container string, name string, heade func (b BlobStorageClient) ChangeLease(container string, name string, currentLeaseID string, proposedLeaseID string) (newLeaseID string, err error) { headers := b.client.getStandardHeaders() headers[leaseAction] = changeLease - headers[leaseID] = currentLeaseID + headers[headerLeaseID] = currentLeaseID headers[leaseProposedID] = proposedLeaseID respHeaders, err := b.leaseCommonPut(container, name, headers, http.StatusOK) @@ -669,7 +440,7 @@ func (b BlobStorageClient) ChangeLease(container string, name string, currentLea return "", err } - newLeaseID = respHeaders.Get(http.CanonicalHeaderKey(leaseID)) + newLeaseID = respHeaders.Get(http.CanonicalHeaderKey(headerLeaseID)) if newLeaseID != "" { return newLeaseID, nil } @@ -681,7 +452,7 @@ func (b BlobStorageClient) ChangeLease(container string, name string, currentLea func (b BlobStorageClient) ReleaseLease(container string, name string, currentLeaseID string) error { headers := b.client.getStandardHeaders() headers[leaseAction] = releaseLease - headers[leaseID] = currentLeaseID + headers[headerLeaseID] = currentLeaseID _, err := b.leaseCommonPut(container, name, headers, http.StatusOK) if err != nil { @@ -695,7 +466,7 @@ func (b BlobStorageClient) ReleaseLease(container string, name string, currentLe func (b BlobStorageClient) RenewLease(container string, name string, currentLeaseID string) error { headers := b.client.getStandardHeaders() headers[leaseAction] = renewLease - headers[leaseID] = currentLeaseID + headers[headerLeaseID] = currentLeaseID _, err := b.leaseCommonPut(container, name, headers, http.StatusOK) if err != nil { @@ -708,17 +479,16 @@ func (b BlobStorageClient) RenewLease(container string, name string, currentLeas // GetBlobProperties provides various information about the specified // blob. See https://msdn.microsoft.com/en-us/library/azure/dd179394.aspx func (b BlobStorageClient) GetBlobProperties(container, name string) (*BlobProperties, error) { - verb := "HEAD" uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) headers := b.client.getStandardHeaders() - resp, err := b.client.exec(verb, uri, headers, nil) + resp, err := b.client.exec(http.MethodHead, uri, headers, nil, b.auth) if err != nil { return nil, err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) - if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { return nil, err } @@ -758,6 +528,7 @@ func (b BlobStorageClient) GetBlobProperties(container, name string) (*BlobPrope CopyStatus: resp.headers.Get("x-ms-copy-status"), BlobType: BlobType(resp.headers.Get("x-ms-blob-type")), LeaseStatus: resp.headers.Get("x-ms-lease-status"), + LeaseState: resp.headers.Get("x-ms-lease-state"), }, nil } @@ -780,11 +551,11 @@ func (b BlobStorageClient) SetBlobProperties(container, name string, blobHeaders headers[k] = v } - resp, err := b.client.exec("PUT", uri, headers, nil) + resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusOK}) } @@ -800,6 +571,8 @@ func (b BlobStorageClient) SetBlobProperties(container, name string, blobHeaders func (b BlobStorageClient) SetBlobMetadata(container, name string, metadata map[string]string, extraHeaders map[string]string) error { params := url.Values{"comp": {"metadata"}} uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) + metadata = b.client.protectUserAgent(metadata) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() for k, v := range metadata { headers[userDefinedMetadataHeaderPrefix+k] = v @@ -809,11 +582,11 @@ func (b BlobStorageClient) SetBlobMetadata(container, name string, metadata map[ headers[k] = v } - resp, err := b.client.exec("PUT", uri, headers, nil) + resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusOK}) } @@ -829,11 +602,11 @@ func (b BlobStorageClient) GetBlobMetadata(container, name string) (map[string]s uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) headers := b.client.getStandardHeaders() - resp, err := b.client.exec("GET", uri, headers, nil) + resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth) if err != nil { return nil, err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { return nil, err @@ -872,7 +645,7 @@ func (b BlobStorageClient) CreateBlockBlob(container, name string) error { // reader. Size must be the number of bytes read from reader. To // create an empty blob, use size==0 and reader==nil. // -// The API rejects requests with size > 64 MiB (but this limit is not +// The API rejects requests with size > 256 MiB (but this limit is not // checked by the SDK). To write a larger blob, use CreateBlockBlob, // PutBlock, and PutBlockList. // @@ -880,6 +653,7 @@ func (b BlobStorageClient) CreateBlockBlob(container, name string) error { func (b BlobStorageClient) CreateBlockBlobFromReader(container, name string, size uint64, blob io.Reader, extraHeaders map[string]string) error { path := fmt.Sprintf("%s/%s", container, name) uri := b.client.getEndpoint(blobServiceName, path, url.Values{}) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() headers["x-ms-blob-type"] = string(BlobTypeBlock) headers["Content-Length"] = fmt.Sprintf("%d", size) @@ -888,18 +662,18 @@ func (b BlobStorageClient) CreateBlockBlobFromReader(container, name string, siz headers[k] = v } - resp, err := b.client.exec("PUT", uri, headers, blob) + resp, err := b.client.exec(http.MethodPut, uri, headers, blob, b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } // PutBlock saves the given data chunk to the specified block blob with // given ID. // -// The API rejects chunks larger than 4 MiB (but this limit is not +// The API rejects chunks larger than 100 MB (but this limit is not // checked by the SDK). // // See https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx @@ -911,12 +685,13 @@ func (b BlobStorageClient) PutBlock(container, name, blockID string, chunk []byt // the block blob with given ID. It is an alternative to PutBlocks where data // comes as stream but the length is known in advance. // -// The API rejects requests with size > 4 MiB (but this limit is not +// The API rejects requests with size > 100 MB (but this limit is not // checked by the SDK). // // See https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx func (b BlobStorageClient) PutBlockWithLength(container, name, blockID string, size uint64, blob io.Reader, extraHeaders map[string]string) error { uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{"comp": {"block"}, "blockid": {blockID}}) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() headers["x-ms-blob-type"] = string(BlobTypeBlock) headers["Content-Length"] = fmt.Sprintf("%v", size) @@ -925,12 +700,12 @@ func (b BlobStorageClient) PutBlockWithLength(container, name, blockID string, s headers[k] = v } - resp, err := b.client.exec("PUT", uri, headers, blob) + resp, err := b.client.exec(http.MethodPut, uri, headers, blob, b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } @@ -944,11 +719,11 @@ func (b BlobStorageClient) PutBlockList(container, name string, blocks []Block) headers := b.client.getStandardHeaders() headers["Content-Length"] = fmt.Sprintf("%v", len(blockListXML)) - resp, err := b.client.exec("PUT", uri, headers, strings.NewReader(blockListXML)) + resp, err := b.client.exec(http.MethodPut, uri, headers, strings.NewReader(blockListXML), b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } @@ -961,7 +736,7 @@ func (b BlobStorageClient) GetBlockList(container, name string, blockType BlockL headers := b.client.getStandardHeaders() var out BlockListResponse - resp, err := b.client.exec("GET", uri, headers, nil) + resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth) if err != nil { return out, err } @@ -979,6 +754,7 @@ func (b BlobStorageClient) GetBlockList(container, name string, blockType BlockL func (b BlobStorageClient) PutPageBlob(container, name string, size int64, extraHeaders map[string]string) error { path := fmt.Sprintf("%s/%s", container, name) uri := b.client.getEndpoint(blobServiceName, path, url.Values{}) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() headers["x-ms-blob-type"] = string(BlobTypePage) headers["x-ms-blob-content-length"] = fmt.Sprintf("%v", size) @@ -987,11 +763,11 @@ func (b BlobStorageClient) PutPageBlob(container, name string, size int64, extra headers[k] = v } - resp, err := b.client.exec("PUT", uri, headers, nil) + resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } @@ -1004,6 +780,7 @@ func (b BlobStorageClient) PutPageBlob(container, name string, size int64, extra func (b BlobStorageClient) PutPage(container, name string, startByte, endByte int64, writeType PageWriteType, chunk []byte, extraHeaders map[string]string) error { path := fmt.Sprintf("%s/%s", container, name) uri := b.client.getEndpoint(blobServiceName, path, url.Values{"comp": {"page"}}) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() headers["x-ms-blob-type"] = string(BlobTypePage) headers["x-ms-page-write"] = string(writeType) @@ -1022,11 +799,11 @@ func (b BlobStorageClient) PutPage(container, name string, startByte, endByte in } headers["Content-Length"] = fmt.Sprintf("%v", contentLength) - resp, err := b.client.exec("PUT", uri, headers, data) + resp, err := b.client.exec(http.MethodPut, uri, headers, data, b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } @@ -1040,13 +817,13 @@ func (b BlobStorageClient) GetPageRanges(container, name string) (GetPageRangesR headers := b.client.getStandardHeaders() var out GetPageRangesResponse - resp, err := b.client.exec("GET", uri, headers, nil) + resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth) if err != nil { return out, err } defer resp.body.Close() - if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { return out, err } err = xmlUnmarshal(resp.body, &out) @@ -1060,6 +837,7 @@ func (b BlobStorageClient) GetPageRanges(container, name string) (GetPageRangesR func (b BlobStorageClient) PutAppendBlob(container, name string, extraHeaders map[string]string) error { path := fmt.Sprintf("%s/%s", container, name) uri := b.client.getEndpoint(blobServiceName, path, url.Values{}) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() headers["x-ms-blob-type"] = string(BlobTypeAppend) @@ -1067,11 +845,11 @@ func (b BlobStorageClient) PutAppendBlob(container, name string, extraHeaders ma headers[k] = v } - resp, err := b.client.exec("PUT", uri, headers, nil) + resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } @@ -1082,6 +860,7 @@ func (b BlobStorageClient) PutAppendBlob(container, name string, extraHeaders ma func (b BlobStorageClient) AppendBlock(container, name string, chunk []byte, extraHeaders map[string]string) error { path := fmt.Sprintf("%s/%s", container, name) uri := b.client.getEndpoint(blobServiceName, path, url.Values{"comp": {"appendblock"}}) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() headers["x-ms-blob-type"] = string(BlobTypeAppend) headers["Content-Length"] = fmt.Sprintf("%v", len(chunk)) @@ -1090,11 +869,11 @@ func (b BlobStorageClient) AppendBlock(container, name string, chunk []byte, ext headers[k] = v } - resp, err := b.client.exec("PUT", uri, headers, bytes.NewReader(chunk)) + resp, err := b.client.exec(http.MethodPut, uri, headers, bytes.NewReader(chunk), b.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } @@ -1106,25 +885,30 @@ func (b BlobStorageClient) AppendBlock(container, name string, chunk []byte, ext // // See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx func (b BlobStorageClient) CopyBlob(container, name, sourceBlob string) error { - copyID, err := b.startBlobCopy(container, name, sourceBlob) + copyID, err := b.StartBlobCopy(container, name, sourceBlob) if err != nil { return err } - return b.waitForBlobCopy(container, name, copyID) + return b.WaitForBlobCopy(container, name, copyID) } -func (b BlobStorageClient) startBlobCopy(container, name, sourceBlob string) (string, error) { +// StartBlobCopy starts a blob copy operation. +// sourceBlob parameter must be a canonical URL to the blob (can be +// obtained using GetBlobURL method.) +// +// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx +func (b BlobStorageClient) StartBlobCopy(container, name, sourceBlob string) (string, error) { uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) headers := b.client.getStandardHeaders() headers["x-ms-copy-source"] = sourceBlob - resp, err := b.client.exec("PUT", uri, headers, nil) + resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth) if err != nil { return "", err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) if err := checkRespCode(resp.statusCode, []int{http.StatusAccepted, http.StatusCreated}); err != nil { return "", err @@ -1137,7 +921,39 @@ func (b BlobStorageClient) startBlobCopy(container, name, sourceBlob string) (st return copyID, nil } -func (b BlobStorageClient) waitForBlobCopy(container, name, copyID string) error { +// AbortBlobCopy aborts a BlobCopy which has already been triggered by the StartBlobCopy function. +// copyID is generated from StartBlobCopy function. +// currentLeaseID is required IF the destination blob has an active lease on it. +// As defined in https://msdn.microsoft.com/en-us/library/azure/jj159098.aspx +func (b BlobStorageClient) AbortBlobCopy(container, name, copyID, currentLeaseID string, timeout int) error { + params := url.Values{"comp": {"copy"}, "copyid": {copyID}} + if timeout > 0 { + params.Add("timeout", strconv.Itoa(timeout)) + } + + uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params) + headers := b.client.getStandardHeaders() + headers["x-ms-copy-action"] = "abort" + + if currentLeaseID != "" { + headers[headerLeaseID] = currentLeaseID + } + + resp, err := b.client.exec(http.MethodPut, uri, headers, nil, b.auth) + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + + if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil { + return err + } + + return nil +} + +// WaitForBlobCopy loops until a BlobCopy operation is completed (or fails with error) +func (b BlobStorageClient) WaitForBlobCopy(container, name, copyID string) error { for { props, err := b.GetBlobProperties(container, name) if err != nil { @@ -1171,7 +987,7 @@ func (b BlobStorageClient) DeleteBlob(container, name string, extraHeaders map[s if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) } @@ -1181,27 +997,24 @@ func (b BlobStorageClient) DeleteBlob(container, name string, extraHeaders map[s // See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx func (b BlobStorageClient) DeleteBlobIfExists(container, name string, extraHeaders map[string]string) (bool, error) { resp, err := b.deleteBlob(container, name, extraHeaders) - if resp != nil && (resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound) { - return resp.statusCode == http.StatusAccepted, nil + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusAccepted, nil + } } - defer resp.body.Close() return false, err } func (b BlobStorageClient) deleteBlob(container, name string, extraHeaders map[string]string) (*storageResponse, error) { - verb := "DELETE" uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) + extraHeaders = b.client.protectUserAgent(extraHeaders) headers := b.client.getStandardHeaders() for k, v := range extraHeaders { headers[k] = v } - return b.client.exec(verb, uri, headers, nil) -} - -// helper method to construct the path to a container given its name -func pathForContainer(name string) string { - return fmt.Sprintf("/%s", name) + return b.client.exec(http.MethodDelete, uri, headers, nil, b.auth) } // helper method to construct the path to a blob given its container and blob @@ -1210,17 +1023,26 @@ func pathForBlob(container, name string) string { return fmt.Sprintf("/%s/%s", container, name) } -// GetBlobSASURI creates an URL to the specified blob which contains the Shared -// Access Signature with specified permissions and expiration time. +// helper method to construct the path to either a blob or container +func pathForResource(container, name string) string { + if len(name) > 0 { + return fmt.Sprintf("/%s/%s", container, name) + } + return fmt.Sprintf("/%s", container) +} + +// GetBlobSASURIWithSignedIPAndProtocol creates an URL to the specified blob which contains the Shared +// Access Signature with specified permissions and expiration time. Also includes signedIPRange and allowed protocols. +// If old API version is used but no signedIP is passed (ie empty string) then this should still work. +// We only populate the signedIP when it non-empty. // // See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx -func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Time, permissions string) (string, error) { +func (b BlobStorageClient) GetBlobSASURIWithSignedIPAndProtocol(container, name string, expiry time.Time, permissions string, signedIPRange string, HTTPSOnly bool) (string, error) { var ( signedPermissions = permissions blobURL = b.GetBlobURL(container, name) ) - canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL) - + canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL, b.auth) if err != nil { return "", err } @@ -1232,16 +1054,24 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim // We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component). canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1) - canonicalizedResource, err = url.QueryUnescape(canonicalizedResource) if err != nil { return "", err } signedExpiry := expiry.UTC().Format(time.RFC3339) - signedResource := "b" - stringToSign, err := blobSASStringToSign(b.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions) + //If blob name is missing, resource is a container + signedResource := "c" + if len(name) > 0 { + signedResource = "b" + } + + protocols := "https,http" + if HTTPSOnly { + protocols = "https" + } + stringToSign, err := blobSASStringToSign(b.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions, signedIPRange, protocols) if err != nil { return "", err } @@ -1255,6 +1085,13 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim "sig": {sig}, } + if b.client.apiVersion >= "2015-04-05" { + sasParams.Add("spr", protocols) + if signedIPRange != "" { + sasParams.Add("sip", signedIPRange) + } + } + sasURL, err := url.Parse(blobURL) if err != nil { return "", err @@ -1263,16 +1100,31 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim return sasURL.String(), nil } -func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string) (string, error) { +// GetBlobSASURI creates an URL to the specified blob which contains the Shared +// Access Signature with specified permissions and expiration time. +// +// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx +func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Time, permissions string) (string, error) { + url, err := b.GetBlobSASURIWithSignedIPAndProtocol(container, name, expiry, permissions, "", false) + return url, err +} + +func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string, signedIP string, protocols string) (string, error) { var signedStart, signedIdentifier, rscc, rscd, rsce, rscl, rsct string if signedVersion >= "2015-02-21" { canonicalizedResource = "/blob" + canonicalizedResource } + // https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx#Anchor_12 + if signedVersion >= "2015-04-05" { + return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion, rscc, rscd, rsce, rscl, rsct), nil + } + // reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx if signedVersion >= "2013-08-15" { return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil } + return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15") } diff --git a/vendor/github.com/Azure/azure-storage-go/blobserviceclient.go b/vendor/github.com/Azure/azure-storage-go/blobserviceclient.go new file mode 100644 index 000000000..e5911ac81 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/blobserviceclient.go @@ -0,0 +1,92 @@ +package storage + +import ( + "fmt" + "net/http" + "net/url" +) + +// BlobStorageClient contains operations for Microsoft Azure Blob Storage +// Service. +type BlobStorageClient struct { + client Client + auth authentication +} + +// GetServiceProperties gets the properties of your storage account's blob service. +// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-blob-service-properties +func (b *BlobStorageClient) GetServiceProperties() (*ServiceProperties, error) { + return b.client.getServiceProperties(blobServiceName, b.auth) +} + +// SetServiceProperties sets the properties of your storage account's blob service. +// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-blob-service-properties +func (b *BlobStorageClient) SetServiceProperties(props ServiceProperties) error { + return b.client.setServiceProperties(props, blobServiceName, b.auth) +} + +// ListContainersParameters defines the set of customizable parameters to make a +// List Containers call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx +type ListContainersParameters struct { + Prefix string + Marker string + Include string + MaxResults uint + Timeout uint +} + +// GetContainerReference returns a Container object for the specified container name. +func (b BlobStorageClient) GetContainerReference(name string) Container { + return Container{ + bsc: &b, + Name: name, + } +} + +// ListContainers returns the list of containers in a storage account along with +// pagination token and other response details. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx +func (b BlobStorageClient) ListContainers(params ListContainersParameters) (*ContainerListResponse, error) { + q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}}) + uri := b.client.getEndpoint(blobServiceName, "", q) + headers := b.client.getStandardHeaders() + + var out ContainerListResponse + resp, err := b.client.exec(http.MethodGet, uri, headers, nil, b.auth) + if err != nil { + return nil, err + } + defer resp.body.Close() + err = xmlUnmarshal(resp.body, &out) + + // assign our client to the newly created Container objects + for i := range out.Containers { + out.Containers[i].bsc = &b + } + return &out, err +} + +func (p ListContainersParameters) getParameters() url.Values { + out := url.Values{} + + if p.Prefix != "" { + out.Set("prefix", p.Prefix) + } + if p.Marker != "" { + out.Set("marker", p.Marker) + } + if p.Include != "" { + out.Set("include", p.Include) + } + if p.MaxResults != 0 { + out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) + } + if p.Timeout != 0 { + out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) + } + + return out +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/client.go b/vendor/github.com/Azure/azure-storage-go/client.go index 2816e03ec..9ddbf08ae 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/storage/client.go +++ b/vendor/github.com/Azure/azure-storage-go/client.go @@ -12,20 +12,21 @@ import ( "io/ioutil" "net/http" "net/url" - "regexp" - "sort" + "runtime" "strconv" "strings" + + "github.com/Azure/go-autorest/autorest/azure" ) const ( - // DefaultBaseURL is the domain name used for storage requests when a - // default client is created. + // DefaultBaseURL is the domain name used for storage requests in the + // public cloud when a default client is created. DefaultBaseURL = "core.windows.net" - // DefaultAPIVersion is the Azure Storage API version string used when a + // DefaultAPIVersion is the Azure Storage API version string used when a // basic client is created. - DefaultAPIVersion = "2015-02-21" + DefaultAPIVersion = "2016-05-31" defaultUseHTTPS = true @@ -43,6 +44,8 @@ const ( storageEmulatorBlob = "127.0.0.1:10000" storageEmulatorTable = "127.0.0.1:10002" storageEmulatorQueue = "127.0.0.1:10001" + + userAgentHeader = "User-Agent" ) // Client is the object that needs to be constructed to perform @@ -52,11 +55,13 @@ type Client struct { // requests. If it is nil, http.DefaultClient is used. HTTPClient *http.Client - accountName string - accountKey []byte - useHTTPS bool - baseURL string - apiVersion string + accountName string + accountKey []byte + useHTTPS bool + UseSharedKeyLite bool + baseURL string + apiVersion string + userAgent string } type storageResponse struct { @@ -130,6 +135,15 @@ func NewBasicClient(accountName, accountKey string) (Client, error) { return NewClient(accountName, accountKey, DefaultBaseURL, DefaultAPIVersion, defaultUseHTTPS) } +// NewBasicClientOnSovereignCloud constructs a Client with given storage service name and +// key in the referenced cloud. +func NewBasicClientOnSovereignCloud(accountName, accountKey string, env azure.Environment) (Client, error) { + if accountName == StorageEmulatorAccountName { + return NewEmulatorClient() + } + return NewClient(accountName, accountKey, env.StorageEndpointSuffix, DefaultAPIVersion, defaultUseHTTPS) +} + //NewEmulatorClient contructs a Client intended to only work with Azure //Storage Emulator func NewEmulatorClient() (Client, error) { @@ -154,13 +168,46 @@ func NewClient(accountName, accountKey, blobServiceBaseURL, apiVersion string, u return c, fmt.Errorf("azure: malformed storage account key: %v", err) } - return Client{ - accountName: accountName, - accountKey: key, - useHTTPS: useHTTPS, - baseURL: blobServiceBaseURL, - apiVersion: apiVersion, - }, nil + c = Client{ + accountName: accountName, + accountKey: key, + useHTTPS: useHTTPS, + baseURL: blobServiceBaseURL, + apiVersion: apiVersion, + UseSharedKeyLite: false, + } + c.userAgent = c.getDefaultUserAgent() + return c, nil +} + +func (c Client) getDefaultUserAgent() string { + return fmt.Sprintf("Go/%s (%s-%s) Azure-SDK-For-Go/%s storage-dataplane/%s", + runtime.Version(), + runtime.GOARCH, + runtime.GOOS, + sdkVersion, + c.apiVersion, + ) +} + +// AddToUserAgent adds an extension to the current user agent +func (c *Client) AddToUserAgent(extension string) error { + if extension != "" { + c.userAgent = fmt.Sprintf("%s %s", c.userAgent, extension) + return nil + } + return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.userAgent) +} + +// protectUserAgent is used in funcs that include extraheaders as a parameter. +// It prevents the User-Agent header to be overwritten, instead if it happens to +// be present, it gets added to the current User-Agent. Use it before getStandardHeaders +func (c *Client) protectUserAgent(extraheaders map[string]string) map[string]string { + if v, ok := extraheaders[userAgentHeader]; ok { + c.AddToUserAgent(v) + delete(extraheaders, userAgentHeader) + } + return extraheaders } func (c Client) getBaseURL(service string) string { @@ -212,181 +259,69 @@ func (c Client) getEndpoint(service, path string, params url.Values) string { // GetBlobService returns a BlobStorageClient which can operate on the blob // service of the storage account. func (c Client) GetBlobService() BlobStorageClient { - return BlobStorageClient{c} + b := BlobStorageClient{ + client: c, + } + b.client.AddToUserAgent(blobServiceName) + b.auth = sharedKey + if c.UseSharedKeyLite { + b.auth = sharedKeyLite + } + return b } // GetQueueService returns a QueueServiceClient which can operate on the queue // service of the storage account. func (c Client) GetQueueService() QueueServiceClient { - return QueueServiceClient{c} + q := QueueServiceClient{ + client: c, + } + q.client.AddToUserAgent(queueServiceName) + q.auth = sharedKey + if c.UseSharedKeyLite { + q.auth = sharedKeyLite + } + return q } // GetTableService returns a TableServiceClient which can operate on the table // service of the storage account. func (c Client) GetTableService() TableServiceClient { - return TableServiceClient{c} + t := TableServiceClient{ + client: c, + } + t.client.AddToUserAgent(tableServiceName) + t.auth = sharedKeyForTable + if c.UseSharedKeyLite { + t.auth = sharedKeyLiteForTable + } + return t } // GetFileService returns a FileServiceClient which can operate on the file // service of the storage account. func (c Client) GetFileService() FileServiceClient { - return FileServiceClient{c} -} - -func (c Client) createAuthorizationHeader(canonicalizedString string) string { - signature := c.computeHmac256(canonicalizedString) - return fmt.Sprintf("%s %s:%s", "SharedKey", c.getCanonicalizedAccountName(), signature) -} - -func (c Client) getAuthorizationHeader(verb, url string, headers map[string]string) (string, error) { - canonicalizedResource, err := c.buildCanonicalizedResource(url) - if err != nil { - return "", err + f := FileServiceClient{ + client: c, } - - canonicalizedString := c.buildCanonicalizedString(verb, headers, canonicalizedResource) - return c.createAuthorizationHeader(canonicalizedString), nil + f.client.AddToUserAgent(fileServiceName) + f.auth = sharedKey + if c.UseSharedKeyLite { + f.auth = sharedKeyLite + } + return f } func (c Client) getStandardHeaders() map[string]string { return map[string]string{ - "x-ms-version": c.apiVersion, - "x-ms-date": currentTimeRfc1123Formatted(), + userAgentHeader: c.userAgent, + "x-ms-version": c.apiVersion, + "x-ms-date": currentTimeRfc1123Formatted(), } } -func (c Client) getCanonicalizedAccountName() string { - // since we may be trying to access a secondary storage account, we need to - // remove the -secondary part of the storage name - return strings.TrimSuffix(c.accountName, "-secondary") -} - -func (c Client) buildCanonicalizedHeader(headers map[string]string) string { - cm := make(map[string]string) - - for k, v := range headers { - headerName := strings.TrimSpace(strings.ToLower(k)) - match, _ := regexp.MatchString("x-ms-", headerName) - if match { - cm[headerName] = v - } - } - - if len(cm) == 0 { - return "" - } - - keys := make([]string, 0, len(cm)) - for key := range cm { - keys = append(keys, key) - } - - sort.Strings(keys) - - ch := "" - - for i, key := range keys { - if i == len(keys)-1 { - ch += fmt.Sprintf("%s:%s", key, cm[key]) - } else { - ch += fmt.Sprintf("%s:%s\n", key, cm[key]) - } - } - return ch -} - -func (c Client) buildCanonicalizedResourceTable(uri string) (string, error) { - errMsg := "buildCanonicalizedResourceTable error: %s" - u, err := url.Parse(uri) - if err != nil { - return "", fmt.Errorf(errMsg, err.Error()) - } - - cr := "/" + c.getCanonicalizedAccountName() - - if len(u.Path) > 0 { - cr += u.Path - } - - return cr, nil -} - -func (c Client) buildCanonicalizedResource(uri string) (string, error) { - errMsg := "buildCanonicalizedResource error: %s" - u, err := url.Parse(uri) - if err != nil { - return "", fmt.Errorf(errMsg, err.Error()) - } - - cr := "/" + c.getCanonicalizedAccountName() - - if len(u.Path) > 0 { - // Any portion of the CanonicalizedResource string that is derived from - // the resource's URI should be encoded exactly as it is in the URI. - // -- https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx - cr += u.EscapedPath() - } - - params, err := url.ParseQuery(u.RawQuery) - if err != nil { - return "", fmt.Errorf(errMsg, err.Error()) - } - - if len(params) > 0 { - cr += "\n" - keys := make([]string, 0, len(params)) - for key := range params { - keys = append(keys, key) - } - - sort.Strings(keys) - - for i, key := range keys { - if len(params[key]) > 1 { - sort.Strings(params[key]) - } - - if i == len(keys)-1 { - cr += fmt.Sprintf("%s:%s", key, strings.Join(params[key], ",")) - } else { - cr += fmt.Sprintf("%s:%s\n", key, strings.Join(params[key], ",")) - } - } - } - - return cr, nil -} - -func (c Client) buildCanonicalizedString(verb string, headers map[string]string, canonicalizedResource string) string { - contentLength := headers["Content-Length"] - if contentLength == "0" { - contentLength = "" - } - canonicalizedString := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", - verb, - headers["Content-Encoding"], - headers["Content-Language"], - contentLength, - headers["Content-MD5"], - headers["Content-Type"], - headers["Date"], - headers["If-Modified-Since"], - headers["If-Match"], - headers["If-None-Match"], - headers["If-Unmodified-Since"], - headers["Range"], - c.buildCanonicalizedHeader(headers), - canonicalizedResource) - - return canonicalizedString -} - -func (c Client) exec(verb, url string, headers map[string]string, body io.Reader) (*storageResponse, error) { - authHeader, err := c.getAuthorizationHeader(verb, url, headers) - if err != nil { - return nil, err - } - headers["Authorization"] = authHeader +func (c Client) exec(verb, url string, headers map[string]string, body io.Reader, auth authentication) (*storageResponse, error) { + headers, err := c.addAuthorizationHeader(verb, url, headers, auth) if err != nil { return nil, err } @@ -422,17 +357,18 @@ func (c Client) exec(verb, url string, headers map[string]string, body io.Reader statusCode := resp.StatusCode if statusCode >= 400 && statusCode <= 505 { var respBody []byte - respBody, err = readResponseBody(resp) + respBody, err = readAndCloseBody(resp.Body) if err != nil { return nil, err } + requestID := resp.Header.Get("x-ms-request-id") if len(respBody) == 0 { - // no error in response body - err = fmt.Errorf("storage: service returned without a response body (%s)", resp.Status) + // no error in response body, might happen in HEAD requests + err = serviceErrFromStatusCode(resp.StatusCode, resp.Status, requestID) } else { // response contains storage service error object, unmarshal - storageErr, errIn := serviceErrFromXML(respBody, resp.StatusCode, resp.Header.Get("x-ms-request-id")) + storageErr, errIn := serviceErrFromXML(respBody, resp.StatusCode, requestID) if err != nil { // error unmarshaling the error response err = errIn } @@ -451,7 +387,12 @@ func (c Client) exec(verb, url string, headers map[string]string, body io.Reader body: resp.Body}, nil } -func (c Client) execInternalJSON(verb, url string, headers map[string]string, body io.Reader) (*odataResponse, error) { +func (c Client) execInternalJSON(verb, url string, headers map[string]string, body io.Reader, auth authentication) (*odataResponse, error) { + headers, err := c.addAuthorizationHeader(verb, url, headers, auth) + if err != nil { + return nil, err + } + req, err := http.NewRequest(verb, url, body) for k, v := range headers { req.Header.Add(k, v) @@ -475,14 +416,14 @@ func (c Client) execInternalJSON(verb, url string, headers map[string]string, bo statusCode := resp.StatusCode if statusCode >= 400 && statusCode <= 505 { var respBody []byte - respBody, err = readResponseBody(resp) + respBody, err = readAndCloseBody(resp.Body) if err != nil { return nil, err } if len(respBody) == 0 { - // no error in response body - err = fmt.Errorf("storage: service returned without a response body (%d)", resp.StatusCode) + // no error in response body, might happen in HEAD requests + err = serviceErrFromStatusCode(resp.StatusCode, resp.Status, resp.Header.Get("x-ms-request-id")) return respToRet, err } // try unmarshal as odata.error json @@ -493,31 +434,9 @@ func (c Client) execInternalJSON(verb, url string, headers map[string]string, bo return respToRet, nil } -func (c Client) createSharedKeyLite(url string, headers map[string]string) (string, error) { - can, err := c.buildCanonicalizedResourceTable(url) - - if err != nil { - return "", err - } - strToSign := headers["x-ms-date"] + "\n" + can - - hmac := c.computeHmac256(strToSign) - return fmt.Sprintf("SharedKeyLite %s:%s", c.accountName, hmac), nil -} - -func (c Client) execTable(verb, url string, headers map[string]string, body io.Reader) (*odataResponse, error) { - var err error - headers["Authorization"], err = c.createSharedKeyLite(url, headers) - if err != nil { - return nil, err - } - - return c.execInternalJSON(verb, url, headers, body) -} - -func readResponseBody(resp *http.Response) ([]byte, error) { - defer resp.Body.Close() - out, err := ioutil.ReadAll(resp.Body) +func readAndCloseBody(body io.ReadCloser) ([]byte, error) { + defer body.Close() + out, err := ioutil.ReadAll(body) if err == io.EOF { err = nil } @@ -534,6 +453,15 @@ func serviceErrFromXML(body []byte, statusCode int, requestID string) (AzureStor return storageErr, nil } +func serviceErrFromStatusCode(code int, status string, requestID string) AzureStorageServiceError { + return AzureStorageServiceError{ + StatusCode: code, + Code: status, + RequestID: requestID, + Message: "no response body was available for error status code", + } +} + func (e AzureStorageServiceError) Error() string { return fmt.Sprintf("storage: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=%s, RequestId=%s, QueryParameterName=%s, QueryParameterValue=%s", e.StatusCode, e.Code, e.Message, e.RequestID, e.QueryParameterName, e.QueryParameterValue) diff --git a/vendor/github.com/Azure/azure-storage-go/container.go b/vendor/github.com/Azure/azure-storage-go/container.go new file mode 100644 index 000000000..f06423967 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/container.go @@ -0,0 +1,376 @@ +package storage + +import ( + "encoding/xml" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "time" +) + +// Container represents an Azure container. +type Container struct { + bsc *BlobStorageClient + Name string `xml:"Name"` + Properties ContainerProperties `xml:"Properties"` +} + +func (c *Container) buildPath() string { + return fmt.Sprintf("/%s", c.Name) +} + +// ContainerProperties contains various properties of a container returned from +// various endpoints like ListContainers. +type ContainerProperties struct { + LastModified string `xml:"Last-Modified"` + Etag string `xml:"Etag"` + LeaseStatus string `xml:"LeaseStatus"` + LeaseState string `xml:"LeaseState"` + LeaseDuration string `xml:"LeaseDuration"` +} + +// ContainerListResponse contains the response fields from +// ListContainers call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx +type ContainerListResponse struct { + XMLName xml.Name `xml:"EnumerationResults"` + Xmlns string `xml:"xmlns,attr"` + Prefix string `xml:"Prefix"` + Marker string `xml:"Marker"` + NextMarker string `xml:"NextMarker"` + MaxResults int64 `xml:"MaxResults"` + Containers []Container `xml:"Containers>Container"` +} + +// BlobListResponse contains the response fields from ListBlobs call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx +type BlobListResponse struct { + XMLName xml.Name `xml:"EnumerationResults"` + Xmlns string `xml:"xmlns,attr"` + Prefix string `xml:"Prefix"` + Marker string `xml:"Marker"` + NextMarker string `xml:"NextMarker"` + MaxResults int64 `xml:"MaxResults"` + Blobs []Blob `xml:"Blobs>Blob"` + + // BlobPrefix is used to traverse blobs as if it were a file system. + // It is returned if ListBlobsParameters.Delimiter is specified. + // The list here can be thought of as "folders" that may contain + // other folders or blobs. + BlobPrefixes []string `xml:"Blobs>BlobPrefix>Name"` + + // Delimiter is used to traverse blobs as if it were a file system. + // It is returned if ListBlobsParameters.Delimiter is specified. + Delimiter string `xml:"Delimiter"` +} + +// ListBlobsParameters defines the set of customizable +// parameters to make a List Blobs call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx +type ListBlobsParameters struct { + Prefix string + Delimiter string + Marker string + Include string + MaxResults uint + Timeout uint +} + +func (p ListBlobsParameters) getParameters() url.Values { + out := url.Values{} + + if p.Prefix != "" { + out.Set("prefix", p.Prefix) + } + if p.Delimiter != "" { + out.Set("delimiter", p.Delimiter) + } + if p.Marker != "" { + out.Set("marker", p.Marker) + } + if p.Include != "" { + out.Set("include", p.Include) + } + if p.MaxResults != 0 { + out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) + } + if p.Timeout != 0 { + out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) + } + + return out +} + +// ContainerAccessType defines the access level to the container from a public +// request. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179468.aspx and "x-ms- +// blob-public-access" header. +type ContainerAccessType string + +// Access options for containers +const ( + ContainerAccessTypePrivate ContainerAccessType = "" + ContainerAccessTypeBlob ContainerAccessType = "blob" + ContainerAccessTypeContainer ContainerAccessType = "container" +) + +// ContainerAccessPolicy represents each access policy in the container ACL. +type ContainerAccessPolicy struct { + ID string + StartTime time.Time + ExpiryTime time.Time + CanRead bool + CanWrite bool + CanDelete bool +} + +// ContainerPermissions represents the container ACLs. +type ContainerPermissions struct { + AccessType ContainerAccessType + AccessPolicies []ContainerAccessPolicy +} + +// ContainerAccessHeader references header used when setting/getting container ACL +const ( + ContainerAccessHeader string = "x-ms-blob-public-access" +) + +// Create creates a blob container within the storage account +// with given name and access level. Returns error if container already exists. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179468.aspx +func (c *Container) Create() error { + resp, err := c.create() + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + return checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// CreateIfNotExists creates a blob container if it does not exist. Returns +// true if container is newly created or false if container already exists. +func (c *Container) CreateIfNotExists() (bool, error) { + resp, err := c.create() + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict { + return resp.statusCode == http.StatusCreated, nil + } + } + return false, err +} + +func (c *Container) create() (*storageResponse, error) { + uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), url.Values{"restype": {"container"}}) + headers := c.bsc.client.getStandardHeaders() + return c.bsc.client.exec(http.MethodPut, uri, headers, nil, c.bsc.auth) +} + +// Exists returns true if a container with given name exists +// on the storage account, otherwise returns false. +func (c *Container) Exists() (bool, error) { + uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), url.Values{"restype": {"container"}}) + headers := c.bsc.client.getStandardHeaders() + + resp, err := c.bsc.client.exec(http.MethodHead, uri, headers, nil, c.bsc.auth) + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusOK, nil + } + } + return false, err +} + +// SetPermissions sets up container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179391.aspx +func (c *Container) SetPermissions(permissions ContainerPermissions, timeout int, leaseID string) error { + params := url.Values{ + "restype": {"container"}, + "comp": {"acl"}, + } + + if timeout > 0 { + params.Add("timeout", strconv.Itoa(timeout)) + } + + uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params) + headers := c.bsc.client.getStandardHeaders() + if permissions.AccessType != "" { + headers[ContainerAccessHeader] = string(permissions.AccessType) + } + + if leaseID != "" { + headers[headerLeaseID] = leaseID + } + + body, length, err := generateContainerACLpayload(permissions.AccessPolicies) + headers["Content-Length"] = strconv.Itoa(length) + + resp, err := c.bsc.client.exec(http.MethodPut, uri, headers, body, c.bsc.auth) + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return errors.New("Unable to set permissions") + } + + return nil +} + +// GetPermissions gets the container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179469.aspx +// If timeout is 0 then it will not be passed to Azure +// leaseID will only be passed to Azure if populated +func (c *Container) GetPermissions(timeout int, leaseID string) (*ContainerPermissions, error) { + params := url.Values{ + "restype": {"container"}, + "comp": {"acl"}, + } + + if timeout > 0 { + params.Add("timeout", strconv.Itoa(timeout)) + } + + uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params) + headers := c.bsc.client.getStandardHeaders() + + if leaseID != "" { + headers[headerLeaseID] = leaseID + } + + resp, err := c.bsc.client.exec(http.MethodGet, uri, headers, nil, c.bsc.auth) + if err != nil { + return nil, err + } + defer resp.body.Close() + + var ap AccessPolicy + err = xmlUnmarshal(resp.body, &ap.SignedIdentifiersList) + if err != nil { + return nil, err + } + return buildAccessPolicy(ap, &resp.headers), nil +} + +func buildAccessPolicy(ap AccessPolicy, headers *http.Header) *ContainerPermissions { + // containerAccess. Blob, Container, empty + containerAccess := headers.Get(http.CanonicalHeaderKey(ContainerAccessHeader)) + permissions := ContainerPermissions{ + AccessType: ContainerAccessType(containerAccess), + AccessPolicies: []ContainerAccessPolicy{}, + } + + for _, policy := range ap.SignedIdentifiersList.SignedIdentifiers { + capd := ContainerAccessPolicy{ + ID: policy.ID, + StartTime: policy.AccessPolicy.StartTime, + ExpiryTime: policy.AccessPolicy.ExpiryTime, + } + capd.CanRead = updatePermissions(policy.AccessPolicy.Permission, "r") + capd.CanWrite = updatePermissions(policy.AccessPolicy.Permission, "w") + capd.CanDelete = updatePermissions(policy.AccessPolicy.Permission, "d") + + permissions.AccessPolicies = append(permissions.AccessPolicies, capd) + } + return &permissions +} + +// Delete deletes the container with given name on the storage +// account. If the container does not exist returns error. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179408.aspx +func (c *Container) Delete() error { + resp, err := c.delete() + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) +} + +// DeleteIfExists deletes the container with given name on the storage +// account if it exists. Returns true if container is deleted with this call, or +// false if the container did not exist at the time of the Delete Container +// operation. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179408.aspx +func (c *Container) DeleteIfExists() (bool, error) { + resp, err := c.delete() + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusAccepted, nil + } + } + return false, err +} + +func (c *Container) delete() (*storageResponse, error) { + uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), url.Values{"restype": {"container"}}) + headers := c.bsc.client.getStandardHeaders() + return c.bsc.client.exec(http.MethodDelete, uri, headers, nil, c.bsc.auth) +} + +// ListBlobs returns an object that contains list of blobs in the container, +// pagination token and other information in the response of List Blobs call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx +func (c *Container) ListBlobs(params ListBlobsParameters) (BlobListResponse, error) { + q := mergeParams(params.getParameters(), url.Values{ + "restype": {"container"}, + "comp": {"list"}}, + ) + uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), q) + headers := c.bsc.client.getStandardHeaders() + + var out BlobListResponse + resp, err := c.bsc.client.exec(http.MethodGet, uri, headers, nil, c.bsc.auth) + if err != nil { + return out, err + } + defer resp.body.Close() + + err = xmlUnmarshal(resp.body, &out) + return out, err +} + +func generateContainerACLpayload(policies []ContainerAccessPolicy) (io.Reader, int, error) { + sil := SignedIdentifiers{ + SignedIdentifiers: []SignedIdentifier{}, + } + for _, capd := range policies { + permission := capd.generateContainerPermissions() + signedIdentifier := convertAccessPolicyToXMLStructs(capd.ID, capd.StartTime, capd.ExpiryTime, permission) + sil.SignedIdentifiers = append(sil.SignedIdentifiers, signedIdentifier) + } + return xmlMarshal(sil) +} + +func (capd *ContainerAccessPolicy) generateContainerPermissions() (permissions string) { + // generate the permissions string (rwd). + // still want the end user API to have bool flags. + permissions = "" + + if capd.CanRead { + permissions += "r" + } + + if capd.CanWrite { + permissions += "w" + } + + if capd.CanDelete { + permissions += "d" + } + + return permissions +} diff --git a/vendor/github.com/Azure/azure-storage-go/directory.go b/vendor/github.com/Azure/azure-storage-go/directory.go new file mode 100644 index 000000000..d27e62079 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/directory.go @@ -0,0 +1,217 @@ +package storage + +import ( + "encoding/xml" + "net/http" + "net/url" +) + +// Directory represents a directory on a share. +type Directory struct { + fsc *FileServiceClient + Metadata map[string]string + Name string `xml:"Name"` + parent *Directory + Properties DirectoryProperties + share *Share +} + +// DirectoryProperties contains various properties of a directory. +type DirectoryProperties struct { + LastModified string `xml:"Last-Modified"` + Etag string `xml:"Etag"` +} + +// ListDirsAndFilesParameters defines the set of customizable parameters to +// make a List Files and Directories call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166980.aspx +type ListDirsAndFilesParameters struct { + Marker string + MaxResults uint + Timeout uint +} + +// DirsAndFilesListResponse contains the response fields from +// a List Files and Directories call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166980.aspx +type DirsAndFilesListResponse struct { + XMLName xml.Name `xml:"EnumerationResults"` + Xmlns string `xml:"xmlns,attr"` + Marker string `xml:"Marker"` + MaxResults int64 `xml:"MaxResults"` + Directories []Directory `xml:"Entries>Directory"` + Files []File `xml:"Entries>File"` + NextMarker string `xml:"NextMarker"` +} + +// builds the complete directory path for this directory object. +func (d *Directory) buildPath() string { + path := "" + current := d + for current.Name != "" { + path = "/" + current.Name + path + current = current.parent + } + return d.share.buildPath() + path +} + +// Create this directory in the associated share. +// If a directory with the same name already exists, the operation fails. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166993.aspx +func (d *Directory) Create() error { + // if this is the root directory exit early + if d.parent == nil { + return nil + } + + headers, err := d.fsc.createResource(d.buildPath(), resourceDirectory, nil, mergeMDIntoExtraHeaders(d.Metadata, nil), []int{http.StatusCreated}) + if err != nil { + return err + } + + d.updateEtagAndLastModified(headers) + return nil +} + +// CreateIfNotExists creates this directory under the associated share if the +// directory does not exists. Returns true if the directory is newly created or +// false if the directory already exists. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166993.aspx +func (d *Directory) CreateIfNotExists() (bool, error) { + // if this is the root directory exit early + if d.parent == nil { + return false, nil + } + + resp, err := d.fsc.createResourceNoClose(d.buildPath(), resourceDirectory, nil, nil) + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict { + if resp.statusCode == http.StatusCreated { + d.updateEtagAndLastModified(resp.headers) + return true, nil + } + + return false, d.FetchAttributes() + } + } + + return false, err +} + +// Delete removes this directory. It must be empty in order to be deleted. +// If the directory does not exist the operation fails. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166969.aspx +func (d *Directory) Delete() error { + return d.fsc.deleteResource(d.buildPath(), resourceDirectory) +} + +// DeleteIfExists removes this directory if it exists. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166969.aspx +func (d *Directory) DeleteIfExists() (bool, error) { + resp, err := d.fsc.deleteResourceNoClose(d.buildPath(), resourceDirectory) + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusAccepted, nil + } + } + return false, err +} + +// Exists returns true if this directory exists. +func (d *Directory) Exists() (bool, error) { + exists, headers, err := d.fsc.resourceExists(d.buildPath(), resourceDirectory) + if exists { + d.updateEtagAndLastModified(headers) + } + return exists, err +} + +// FetchAttributes retrieves metadata for this directory. +func (d *Directory) FetchAttributes() error { + headers, err := d.fsc.getResourceHeaders(d.buildPath(), compNone, resourceDirectory, http.MethodHead) + if err != nil { + return err + } + + d.updateEtagAndLastModified(headers) + d.Metadata = getMetadataFromHeaders(headers) + + return nil +} + +// GetDirectoryReference returns a child Directory object for this directory. +func (d *Directory) GetDirectoryReference(name string) *Directory { + return &Directory{ + fsc: d.fsc, + Name: name, + parent: d, + share: d.share, + } +} + +// GetFileReference returns a child File object for this directory. +func (d *Directory) GetFileReference(name string) *File { + return &File{ + fsc: d.fsc, + Name: name, + parent: d, + share: d.share, + } +} + +// ListDirsAndFiles returns a list of files and directories under this directory. +// It also contains a pagination token and other response details. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166980.aspx +func (d *Directory) ListDirsAndFiles(params ListDirsAndFilesParameters) (*DirsAndFilesListResponse, error) { + q := mergeParams(params.getParameters(), getURLInitValues(compList, resourceDirectory)) + + resp, err := d.fsc.listContent(d.buildPath(), q, nil) + if err != nil { + return nil, err + } + + defer resp.body.Close() + var out DirsAndFilesListResponse + err = xmlUnmarshal(resp.body, &out) + return &out, err +} + +// SetMetadata replaces the metadata for this directory. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by GetDirectoryMetadata. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/mt427370.aspx +func (d *Directory) SetMetadata() error { + headers, err := d.fsc.setResourceHeaders(d.buildPath(), compMetadata, resourceDirectory, mergeMDIntoExtraHeaders(d.Metadata, nil)) + if err != nil { + return err + } + + d.updateEtagAndLastModified(headers) + return nil +} + +// updates Etag and last modified date +func (d *Directory) updateEtagAndLastModified(headers http.Header) { + d.Properties.Etag = headers.Get("Etag") + d.Properties.LastModified = headers.Get("Last-Modified") +} + +// URL gets the canonical URL to this directory. +// This method does not create a publicly accessible URL if the directory +// is private and this method does not check if the directory exists. +func (d *Directory) URL() string { + return d.fsc.client.getEndpoint(fileServiceName, d.buildPath(), url.Values{}) +} diff --git a/vendor/github.com/Azure/azure-storage-go/file.go b/vendor/github.com/Azure/azure-storage-go/file.go new file mode 100644 index 000000000..e4901a114 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/file.go @@ -0,0 +1,412 @@ +package storage + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" +) + +const fourMB = uint64(4194304) +const oneTB = uint64(1099511627776) + +// File represents a file on a share. +type File struct { + fsc *FileServiceClient + Metadata map[string]string + Name string `xml:"Name"` + parent *Directory + Properties FileProperties `xml:"Properties"` + share *Share + FileCopyProperties FileCopyState +} + +// FileProperties contains various properties of a file. +type FileProperties struct { + CacheControl string `header:"x-ms-cache-control"` + Disposition string `header:"x-ms-content-disposition"` + Encoding string `header:"x-ms-content-encoding"` + Etag string + Language string `header:"x-ms-content-language"` + LastModified string + Length uint64 `xml:"Content-Length"` + MD5 string `header:"x-ms-content-md5"` + Type string `header:"x-ms-content-type"` +} + +// FileCopyState contains various properties of a file copy operation. +type FileCopyState struct { + CompletionTime string + ID string `header:"x-ms-copy-id"` + Progress string + Source string + Status string `header:"x-ms-copy-status"` + StatusDesc string +} + +// FileStream contains file data returned from a call to GetFile. +type FileStream struct { + Body io.ReadCloser + ContentMD5 string +} + +// FileRequestOptions will be passed to misc file operations. +// Currently just Timeout (in seconds) but will expand. +type FileRequestOptions struct { + Timeout uint // timeout duration in seconds. +} + +// getParameters, construct parameters for FileRequestOptions. +// currently only timeout, but expecting to grow as functionality fills out. +func (p FileRequestOptions) getParameters() url.Values { + out := url.Values{} + + if p.Timeout != 0 { + out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) + } + + return out +} + +// FileRanges contains a list of file range information for a file. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166984.aspx +type FileRanges struct { + ContentLength uint64 + LastModified string + ETag string + FileRanges []FileRange `xml:"Range"` +} + +// FileRange contains range information for a file. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166984.aspx +type FileRange struct { + Start uint64 `xml:"Start"` + End uint64 `xml:"End"` +} + +func (fr FileRange) String() string { + return fmt.Sprintf("bytes=%d-%d", fr.Start, fr.End) +} + +// builds the complete file path for this file object +func (f *File) buildPath() string { + return f.parent.buildPath() + "/" + f.Name +} + +// ClearRange releases the specified range of space in a file. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn194276.aspx +func (f *File) ClearRange(fileRange FileRange) error { + headers, err := f.modifyRange(nil, fileRange, nil) + if err != nil { + return err + } + + f.updateEtagAndLastModified(headers) + return nil +} + +// Create creates a new file or replaces an existing one. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn194271.aspx +func (f *File) Create(maxSize uint64) error { + if maxSize > oneTB { + return fmt.Errorf("max file size is 1TB") + } + + extraHeaders := map[string]string{ + "x-ms-content-length": strconv.FormatUint(maxSize, 10), + "x-ms-type": "file", + } + + headers, err := f.fsc.createResource(f.buildPath(), resourceFile, nil, mergeMDIntoExtraHeaders(f.Metadata, extraHeaders), []int{http.StatusCreated}) + if err != nil { + return err + } + + f.Properties.Length = maxSize + f.updateEtagAndLastModified(headers) + return nil +} + +// CopyFile operation copied a file/blob from the sourceURL to the path provided. +// +// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/copy-file +func (f *File) CopyFile(sourceURL string, options *FileRequestOptions) error { + extraHeaders := map[string]string{ + "x-ms-type": "file", + "x-ms-copy-source": sourceURL, + } + + var parameters url.Values + if options != nil { + parameters = options.getParameters() + } + + headers, err := f.fsc.createResource(f.buildPath(), resourceFile, parameters, mergeMDIntoExtraHeaders(f.Metadata, extraHeaders), []int{http.StatusAccepted}) + if err != nil { + return err + } + + f.updateEtagLastModifiedAndCopyHeaders(headers) + return nil +} + +// Delete immediately removes this file from the storage account. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn689085.aspx +func (f *File) Delete() error { + return f.fsc.deleteResource(f.buildPath(), resourceFile) +} + +// DeleteIfExists removes this file if it exists. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn689085.aspx +func (f *File) DeleteIfExists() (bool, error) { + resp, err := f.fsc.deleteResourceNoClose(f.buildPath(), resourceFile) + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusAccepted, nil + } + } + return false, err +} + +// DownloadRangeToStream operation downloads the specified range of this file with optional MD5 hash. +// +// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file +func (f *File) DownloadRangeToStream(fileRange FileRange, getContentMD5 bool) (fs FileStream, err error) { + if getContentMD5 && isRangeTooBig(fileRange) { + return fs, fmt.Errorf("must specify a range less than or equal to 4MB when getContentMD5 is true") + } + + extraHeaders := map[string]string{ + "Range": fileRange.String(), + } + if getContentMD5 == true { + extraHeaders["x-ms-range-get-content-md5"] = "true" + } + + resp, err := f.fsc.getResourceNoClose(f.buildPath(), compNone, resourceFile, http.MethodGet, extraHeaders) + if err != nil { + return fs, err + } + + if err = checkRespCode(resp.statusCode, []int{http.StatusOK, http.StatusPartialContent}); err != nil { + resp.body.Close() + return fs, err + } + + fs.Body = resp.body + if getContentMD5 { + fs.ContentMD5 = resp.headers.Get("Content-MD5") + } + return fs, nil +} + +// Exists returns true if this file exists. +func (f *File) Exists() (bool, error) { + exists, headers, err := f.fsc.resourceExists(f.buildPath(), resourceFile) + if exists { + f.updateEtagAndLastModified(headers) + f.updateProperties(headers) + } + return exists, err +} + +// FetchAttributes updates metadata and properties for this file. +func (f *File) FetchAttributes() error { + headers, err := f.fsc.getResourceHeaders(f.buildPath(), compNone, resourceFile, http.MethodHead) + if err != nil { + return err + } + + f.updateEtagAndLastModified(headers) + f.updateProperties(headers) + f.Metadata = getMetadataFromHeaders(headers) + return nil +} + +// returns true if the range is larger than 4MB +func isRangeTooBig(fileRange FileRange) bool { + if fileRange.End-fileRange.Start > fourMB { + return true + } + + return false +} + +// ListRanges returns the list of valid ranges for this file. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166984.aspx +func (f *File) ListRanges(listRange *FileRange) (*FileRanges, error) { + params := url.Values{"comp": {"rangelist"}} + + // add optional range to list + var headers map[string]string + if listRange != nil { + headers = make(map[string]string) + headers["Range"] = listRange.String() + } + + resp, err := f.fsc.listContent(f.buildPath(), params, headers) + if err != nil { + return nil, err + } + + defer resp.body.Close() + var cl uint64 + cl, err = strconv.ParseUint(resp.headers.Get("x-ms-content-length"), 10, 64) + if err != nil { + ioutil.ReadAll(resp.body) + return nil, err + } + + var out FileRanges + out.ContentLength = cl + out.ETag = resp.headers.Get("ETag") + out.LastModified = resp.headers.Get("Last-Modified") + + err = xmlUnmarshal(resp.body, &out) + return &out, err +} + +// modifies a range of bytes in this file +func (f *File) modifyRange(bytes io.Reader, fileRange FileRange, contentMD5 *string) (http.Header, error) { + if err := f.fsc.checkForStorageEmulator(); err != nil { + return nil, err + } + if fileRange.End < fileRange.Start { + return nil, errors.New("the value for rangeEnd must be greater than or equal to rangeStart") + } + if bytes != nil && isRangeTooBig(fileRange) { + return nil, errors.New("range cannot exceed 4MB in size") + } + + uri := f.fsc.client.getEndpoint(fileServiceName, f.buildPath(), url.Values{"comp": {"range"}}) + + // default to clear + write := "clear" + cl := uint64(0) + + // if bytes is not nil then this is an update operation + if bytes != nil { + write = "update" + cl = (fileRange.End - fileRange.Start) + 1 + } + + extraHeaders := map[string]string{ + "Content-Length": strconv.FormatUint(cl, 10), + "Range": fileRange.String(), + "x-ms-write": write, + } + + if contentMD5 != nil { + extraHeaders["Content-MD5"] = *contentMD5 + } + + headers := mergeHeaders(f.fsc.client.getStandardHeaders(), extraHeaders) + resp, err := f.fsc.client.exec(http.MethodPut, uri, headers, bytes, f.fsc.auth) + if err != nil { + return nil, err + } + defer readAndCloseBody(resp.body) + return resp.headers, checkRespCode(resp.statusCode, []int{http.StatusCreated}) +} + +// SetMetadata replaces the metadata for this file. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by GetFileMetadata. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn689097.aspx +func (f *File) SetMetadata() error { + headers, err := f.fsc.setResourceHeaders(f.buildPath(), compMetadata, resourceFile, mergeMDIntoExtraHeaders(f.Metadata, nil)) + if err != nil { + return err + } + + f.updateEtagAndLastModified(headers) + return nil +} + +// SetProperties sets system properties on this file. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by SetFileProperties. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn166975.aspx +func (f *File) SetProperties() error { + headers, err := f.fsc.setResourceHeaders(f.buildPath(), compProperties, resourceFile, headersFromStruct(f.Properties)) + if err != nil { + return err + } + + f.updateEtagAndLastModified(headers) + return nil +} + +// updates Etag and last modified date +func (f *File) updateEtagAndLastModified(headers http.Header) { + f.Properties.Etag = headers.Get("Etag") + f.Properties.LastModified = headers.Get("Last-Modified") +} + +// updates Etag, last modified date and x-ms-copy-id +func (f *File) updateEtagLastModifiedAndCopyHeaders(headers http.Header) { + f.Properties.Etag = headers.Get("Etag") + f.Properties.LastModified = headers.Get("Last-Modified") + f.FileCopyProperties.ID = headers.Get("X-Ms-Copy-Id") + f.FileCopyProperties.Status = headers.Get("X-Ms-Copy-Status") +} + +// updates file properties from the specified HTTP header +func (f *File) updateProperties(header http.Header) { + size, err := strconv.ParseUint(header.Get("Content-Length"), 10, 64) + if err == nil { + f.Properties.Length = size + } + + f.updateEtagAndLastModified(header) + f.Properties.CacheControl = header.Get("Cache-Control") + f.Properties.Disposition = header.Get("Content-Disposition") + f.Properties.Encoding = header.Get("Content-Encoding") + f.Properties.Language = header.Get("Content-Language") + f.Properties.MD5 = header.Get("Content-MD5") + f.Properties.Type = header.Get("Content-Type") +} + +// URL gets the canonical URL to this file. +// This method does not create a publicly accessible URL if the file +// is private and this method does not check if the file exists. +func (f *File) URL() string { + return f.fsc.client.getEndpoint(fileServiceName, f.buildPath(), url.Values{}) +} + +// WriteRange writes a range of bytes to this file with an optional MD5 hash of the content. +// Note that the length of bytes must match (rangeEnd - rangeStart) + 1 with a maximum size of 4MB. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn194276.aspx +func (f *File) WriteRange(bytes io.Reader, fileRange FileRange, contentMD5 *string) error { + if bytes == nil { + return errors.New("bytes cannot be nil") + } + + headers, err := f.modifyRange(bytes, fileRange, contentMD5) + if err != nil { + return err + } + + f.updateEtagAndLastModified(headers) + return nil +} diff --git a/vendor/github.com/Azure/azure-storage-go/fileserviceclient.go b/vendor/github.com/Azure/azure-storage-go/fileserviceclient.go new file mode 100644 index 000000000..d68bd7f64 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/fileserviceclient.go @@ -0,0 +1,375 @@ +package storage + +import ( + "encoding/xml" + "fmt" + "net/http" + "net/url" + "strings" +) + +// FileServiceClient contains operations for Microsoft Azure File Service. +type FileServiceClient struct { + client Client + auth authentication +} + +// ListSharesParameters defines the set of customizable parameters to make a +// List Shares call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn167009.aspx +type ListSharesParameters struct { + Prefix string + Marker string + Include string + MaxResults uint + Timeout uint +} + +// ShareListResponse contains the response fields from +// ListShares call. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn167009.aspx +type ShareListResponse struct { + XMLName xml.Name `xml:"EnumerationResults"` + Xmlns string `xml:"xmlns,attr"` + Prefix string `xml:"Prefix"` + Marker string `xml:"Marker"` + NextMarker string `xml:"NextMarker"` + MaxResults int64 `xml:"MaxResults"` + Shares []Share `xml:"Shares>Share"` +} + +type compType string + +const ( + compNone compType = "" + compList compType = "list" + compMetadata compType = "metadata" + compProperties compType = "properties" + compRangeList compType = "rangelist" +) + +func (ct compType) String() string { + return string(ct) +} + +type resourceType string + +const ( + resourceDirectory resourceType = "directory" + resourceFile resourceType = "" + resourceShare resourceType = "share" +) + +func (rt resourceType) String() string { + return string(rt) +} + +func (p ListSharesParameters) getParameters() url.Values { + out := url.Values{} + + if p.Prefix != "" { + out.Set("prefix", p.Prefix) + } + if p.Marker != "" { + out.Set("marker", p.Marker) + } + if p.Include != "" { + out.Set("include", p.Include) + } + if p.MaxResults != 0 { + out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) + } + if p.Timeout != 0 { + out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) + } + + return out +} + +func (p ListDirsAndFilesParameters) getParameters() url.Values { + out := url.Values{} + + if p.Marker != "" { + out.Set("marker", p.Marker) + } + if p.MaxResults != 0 { + out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults)) + } + if p.Timeout != 0 { + out.Set("timeout", fmt.Sprintf("%v", p.Timeout)) + } + + return out +} + +// returns url.Values for the specified types +func getURLInitValues(comp compType, res resourceType) url.Values { + values := url.Values{} + if comp != compNone { + values.Set("comp", comp.String()) + } + if res != resourceFile { + values.Set("restype", res.String()) + } + return values +} + +// GetShareReference returns a Share object for the specified share name. +func (f FileServiceClient) GetShareReference(name string) Share { + return Share{ + fsc: &f, + Name: name, + Properties: ShareProperties{ + Quota: -1, + }, + } +} + +// ListShares returns the list of shares in a storage account along with +// pagination token and other response details. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx +func (f FileServiceClient) ListShares(params ListSharesParameters) (*ShareListResponse, error) { + q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}}) + + var out ShareListResponse + resp, err := f.listContent("", q, nil) + if err != nil { + return nil, err + } + defer resp.body.Close() + err = xmlUnmarshal(resp.body, &out) + + // assign our client to the newly created Share objects + for i := range out.Shares { + out.Shares[i].fsc = &f + } + return &out, err +} + +// GetServiceProperties gets the properties of your storage account's file service. +// File service does not support logging +// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file-service-properties +func (f *FileServiceClient) GetServiceProperties() (*ServiceProperties, error) { + return f.client.getServiceProperties(fileServiceName, f.auth) +} + +// SetServiceProperties sets the properties of your storage account's file service. +// File service does not support logging +// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-file-service-properties +func (f *FileServiceClient) SetServiceProperties(props ServiceProperties) error { + return f.client.setServiceProperties(props, fileServiceName, f.auth) +} + +// retrieves directory or share content +func (f FileServiceClient) listContent(path string, params url.Values, extraHeaders map[string]string) (*storageResponse, error) { + if err := f.checkForStorageEmulator(); err != nil { + return nil, err + } + + uri := f.client.getEndpoint(fileServiceName, path, params) + extraHeaders = f.client.protectUserAgent(extraHeaders) + headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders) + + resp, err := f.client.exec(http.MethodGet, uri, headers, nil, f.auth) + if err != nil { + return nil, err + } + + if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + readAndCloseBody(resp.body) + return nil, err + } + + return resp, nil +} + +// returns true if the specified resource exists +func (f FileServiceClient) resourceExists(path string, res resourceType) (bool, http.Header, error) { + if err := f.checkForStorageEmulator(); err != nil { + return false, nil, err + } + + uri := f.client.getEndpoint(fileServiceName, path, getURLInitValues(compNone, res)) + headers := f.client.getStandardHeaders() + + resp, err := f.client.exec(http.MethodHead, uri, headers, nil, f.auth) + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusOK, resp.headers, nil + } + } + return false, nil, err +} + +// creates a resource depending on the specified resource type +func (f FileServiceClient) createResource(path string, res resourceType, urlParams url.Values, extraHeaders map[string]string, expectedResponseCodes []int) (http.Header, error) { + resp, err := f.createResourceNoClose(path, res, urlParams, extraHeaders) + if err != nil { + return nil, err + } + defer readAndCloseBody(resp.body) + return resp.headers, checkRespCode(resp.statusCode, expectedResponseCodes) +} + +// creates a resource depending on the specified resource type, doesn't close the response body +func (f FileServiceClient) createResourceNoClose(path string, res resourceType, urlParams url.Values, extraHeaders map[string]string) (*storageResponse, error) { + if err := f.checkForStorageEmulator(); err != nil { + return nil, err + } + + values := getURLInitValues(compNone, res) + combinedParams := mergeParams(values, urlParams) + uri := f.client.getEndpoint(fileServiceName, path, combinedParams) + extraHeaders = f.client.protectUserAgent(extraHeaders) + headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders) + + return f.client.exec(http.MethodPut, uri, headers, nil, f.auth) +} + +// returns HTTP header data for the specified directory or share +func (f FileServiceClient) getResourceHeaders(path string, comp compType, res resourceType, verb string) (http.Header, error) { + resp, err := f.getResourceNoClose(path, comp, res, verb, nil) + if err != nil { + return nil, err + } + defer readAndCloseBody(resp.body) + + if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, err + } + + return resp.headers, nil +} + +// gets the specified resource, doesn't close the response body +func (f FileServiceClient) getResourceNoClose(path string, comp compType, res resourceType, verb string, extraHeaders map[string]string) (*storageResponse, error) { + if err := f.checkForStorageEmulator(); err != nil { + return nil, err + } + + params := getURLInitValues(comp, res) + uri := f.client.getEndpoint(fileServiceName, path, params) + extraHeaders = f.client.protectUserAgent(extraHeaders) + headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders) + + return f.client.exec(verb, uri, headers, nil, f.auth) +} + +// deletes the resource and returns the response +func (f FileServiceClient) deleteResource(path string, res resourceType) error { + resp, err := f.deleteResourceNoClose(path, res) + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) +} + +// deletes the resource and returns the response, doesn't close the response body +func (f FileServiceClient) deleteResourceNoClose(path string, res resourceType) (*storageResponse, error) { + if err := f.checkForStorageEmulator(); err != nil { + return nil, err + } + + values := getURLInitValues(compNone, res) + uri := f.client.getEndpoint(fileServiceName, path, values) + return f.client.exec(http.MethodDelete, uri, f.client.getStandardHeaders(), nil, f.auth) +} + +// merges metadata into extraHeaders and returns extraHeaders +func mergeMDIntoExtraHeaders(metadata, extraHeaders map[string]string) map[string]string { + if metadata == nil && extraHeaders == nil { + return nil + } + if extraHeaders == nil { + extraHeaders = make(map[string]string) + } + for k, v := range metadata { + extraHeaders[userDefinedMetadataHeaderPrefix+k] = v + } + return extraHeaders +} + +// merges extraHeaders into headers and returns headers +func mergeHeaders(headers, extraHeaders map[string]string) map[string]string { + for k, v := range extraHeaders { + headers[k] = v + } + return headers +} + +// sets extra header data for the specified resource +func (f FileServiceClient) setResourceHeaders(path string, comp compType, res resourceType, extraHeaders map[string]string) (http.Header, error) { + if err := f.checkForStorageEmulator(); err != nil { + return nil, err + } + + params := getURLInitValues(comp, res) + uri := f.client.getEndpoint(fileServiceName, path, params) + extraHeaders = f.client.protectUserAgent(extraHeaders) + headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders) + + resp, err := f.client.exec(http.MethodPut, uri, headers, nil, f.auth) + if err != nil { + return nil, err + } + defer readAndCloseBody(resp.body) + + return resp.headers, checkRespCode(resp.statusCode, []int{http.StatusOK}) +} + +// gets metadata for the specified resource +func (f FileServiceClient) getMetadata(path string, res resourceType) (map[string]string, error) { + if err := f.checkForStorageEmulator(); err != nil { + return nil, err + } + + headers, err := f.getResourceHeaders(path, compMetadata, res, http.MethodGet) + if err != nil { + return nil, err + } + + return getMetadataFromHeaders(headers), nil +} + +// returns a map of custom metadata values from the specified HTTP header +func getMetadataFromHeaders(header http.Header) map[string]string { + metadata := make(map[string]string) + for k, v := range header { + // Can't trust CanonicalHeaderKey() to munge case + // reliably. "_" is allowed in identifiers: + // https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx + // https://msdn.microsoft.com/library/aa664670(VS.71).aspx + // http://tools.ietf.org/html/rfc7230#section-3.2 + // ...but "_" is considered invalid by + // CanonicalMIMEHeaderKey in + // https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542 + // so k can be "X-Ms-Meta-Foo" or "x-ms-meta-foo_bar". + k = strings.ToLower(k) + if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) { + continue + } + // metadata["foo"] = content of the last X-Ms-Meta-Foo header + k = k[len(userDefinedMetadataHeaderPrefix):] + metadata[k] = v[len(v)-1] + } + + if len(metadata) == 0 { + return nil + } + + return metadata +} + +//checkForStorageEmulator determines if the client is setup for use with +//Azure Storage Emulator, and returns a relevant error +func (f FileServiceClient) checkForStorageEmulator() error { + if f.client.accountName == StorageEmulatorAccountName { + return fmt.Errorf("Error: File service is not currently supported by Azure Storage Emulator") + } + return nil +} diff --git a/vendor/github.com/Azure/azure-storage-go/glide.lock b/vendor/github.com/Azure/azure-storage-go/glide.lock new file mode 100644 index 000000000..5d3ce8361 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/glide.lock @@ -0,0 +1,14 @@ +hash: a97c0c90fe4d23bbd8e5745431f633e75530bb611131b786d76b8e1763bce85e +updated: 2017-02-23T09:58:57.3701584-08:00 +imports: +- name: github.com/Azure/go-autorest + version: ec5f4903f77ed9927ac95b19ab8e44ada64c1356 + subpackages: + - autorest/azure + - autorest + - autorest/date +- name: github.com/dgrijalva/jwt-go + version: 2268707a8f0843315e2004ee4f1d021dc08baedf +testImports: +- name: gopkg.in/check.v1 + version: 20d25e2804050c1cd24a7eea1e7a6447dd0e74ec diff --git a/vendor/github.com/Azure/azure-storage-go/glide.yaml b/vendor/github.com/Azure/azure-storage-go/glide.yaml new file mode 100644 index 000000000..e6783b774 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/glide.yaml @@ -0,0 +1,4 @@ +package: github.com/Azure/azure-sdk-for-go-storage +import: [] +testImport: +- package: gopkg.in/check.v1 diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go b/vendor/github.com/Azure/azure-storage-go/queue.go index 3ecf4aca0..4031410ae 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/storage/queue.go +++ b/vendor/github.com/Azure/azure-storage-go/queue.go @@ -15,12 +15,6 @@ const ( userDefinedMetadataHeaderPrefix = "X-Ms-Meta-" ) -// QueueServiceClient contains operations for Microsoft Azure Queue Storage -// Service. -type QueueServiceClient struct { - client Client -} - func pathForQueue(queue string) string { return fmt.Sprintf("/%s", queue) } func pathForQueueMessages(queue string) string { return fmt.Sprintf("/%s/messages", queue) } func pathForMessage(queue, name string) string { return fmt.Sprintf("/%s/messages/%s", queue, name) } @@ -82,6 +76,24 @@ func (p PeekMessagesParameters) getParameters() url.Values { return out } +// UpdateMessageParameters is the set of options can be specified for Update Messsage +// operation. A zero struct does not use any preferences for the request. +type UpdateMessageParameters struct { + PopReceipt string + VisibilityTimeout int +} + +func (p UpdateMessageParameters) getParameters() url.Values { + out := url.Values{} + if p.PopReceipt != "" { + out.Set("popreceipt", p.PopReceipt) + } + if p.VisibilityTimeout != 0 { + out.Set("visibilitytimeout", strconv.Itoa(p.VisibilityTimeout)) + } + return out +} + // GetMessagesResponse represents a response returned from Get Messages // operation. type GetMessagesResponse struct { @@ -133,16 +145,17 @@ type QueueMetadataResponse struct { // See https://msdn.microsoft.com/en-us/library/azure/dd179348.aspx func (c QueueServiceClient) SetMetadata(name string, metadata map[string]string) error { uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": []string{"metadata"}}) + metadata = c.client.protectUserAgent(metadata) headers := c.client.getStandardHeaders() for k, v := range metadata { headers[userDefinedMetadataHeaderPrefix+k] = v } - resp, err := c.client.exec("PUT", uri, headers, nil) + resp, err := c.client.exec(http.MethodPut, uri, headers, nil, c.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) } @@ -161,11 +174,11 @@ func (c QueueServiceClient) GetMetadata(name string) (QueueMetadataResponse, err qm.UserDefinedMetadata = make(map[string]string) uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": []string{"metadata"}}) headers := c.client.getStandardHeaders() - resp, err := c.client.exec("GET", uri, headers, nil) + resp, err := c.client.exec(http.MethodGet, uri, headers, nil, c.auth) if err != nil { return qm, err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) for k, v := range resp.headers { if len(v) != 1 { @@ -194,11 +207,11 @@ func (c QueueServiceClient) GetMetadata(name string) (QueueMetadataResponse, err func (c QueueServiceClient) CreateQueue(name string) error { uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{}) headers := c.client.getStandardHeaders() - resp, err := c.client.exec("PUT", uri, headers, nil) + resp, err := c.client.exec(http.MethodPut, uri, headers, nil, c.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } @@ -207,18 +220,18 @@ func (c QueueServiceClient) CreateQueue(name string) error { // See https://msdn.microsoft.com/en-us/library/azure/dd179436.aspx func (c QueueServiceClient) DeleteQueue(name string) error { uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{}) - resp, err := c.client.exec("DELETE", uri, c.client.getStandardHeaders(), nil) + resp, err := c.client.exec(http.MethodDelete, uri, c.client.getStandardHeaders(), nil, c.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) } // QueueExists returns true if a queue with given name exists. func (c QueueServiceClient) QueueExists(name string) (bool, error) { uri := c.client.getEndpoint(queueServiceName, pathForQueue(name), url.Values{"comp": {"metadata"}}) - resp, err := c.client.exec("GET", uri, c.client.getStandardHeaders(), nil) + resp, err := c.client.exec(http.MethodGet, uri, c.client.getStandardHeaders(), nil, c.auth) if resp != nil && (resp.statusCode == http.StatusOK || resp.statusCode == http.StatusNotFound) { return resp.statusCode == http.StatusOK, nil } @@ -238,11 +251,11 @@ func (c QueueServiceClient) PutMessage(queue string, message string, params PutM } headers := c.client.getStandardHeaders() headers["Content-Length"] = strconv.Itoa(nn) - resp, err := c.client.exec("POST", uri, headers, body) + resp, err := c.client.exec(http.MethodPost, uri, headers, body, c.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusCreated}) } @@ -251,11 +264,11 @@ func (c QueueServiceClient) PutMessage(queue string, message string, params PutM // See https://msdn.microsoft.com/en-us/library/azure/dd179454.aspx func (c QueueServiceClient) ClearMessages(queue string) error { uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), url.Values{}) - resp, err := c.client.exec("DELETE", uri, c.client.getStandardHeaders(), nil) + resp, err := c.client.exec(http.MethodDelete, uri, c.client.getStandardHeaders(), nil, c.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) } @@ -266,7 +279,7 @@ func (c QueueServiceClient) ClearMessages(queue string) error { func (c QueueServiceClient) GetMessages(queue string, params GetMessagesParameters) (GetMessagesResponse, error) { var r GetMessagesResponse uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), params.getParameters()) - resp, err := c.client.exec("GET", uri, c.client.getStandardHeaders(), nil) + resp, err := c.client.exec(http.MethodGet, uri, c.client.getStandardHeaders(), nil, c.auth) if err != nil { return r, err } @@ -282,7 +295,7 @@ func (c QueueServiceClient) GetMessages(queue string, params GetMessagesParamete func (c QueueServiceClient) PeekMessages(queue string, params PeekMessagesParameters) (PeekMessagesResponse, error) { var r PeekMessagesResponse uri := c.client.getEndpoint(queueServiceName, pathForQueueMessages(queue), params.getParameters()) - resp, err := c.client.exec("GET", uri, c.client.getStandardHeaders(), nil) + resp, err := c.client.exec(http.MethodGet, uri, c.client.getStandardHeaders(), nil, c.auth) if err != nil { return r, err } @@ -297,10 +310,30 @@ func (c QueueServiceClient) PeekMessages(queue string, params PeekMessagesParame func (c QueueServiceClient) DeleteMessage(queue, messageID, popReceipt string) error { uri := c.client.getEndpoint(queueServiceName, pathForMessage(queue, messageID), url.Values{ "popreceipt": {popReceipt}}) - resp, err := c.client.exec("DELETE", uri, c.client.getStandardHeaders(), nil) + resp, err := c.client.exec(http.MethodDelete, uri, c.client.getStandardHeaders(), nil, c.auth) if err != nil { return err } - defer resp.body.Close() + defer readAndCloseBody(resp.body) + return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) +} + +// UpdateMessage operation deletes the specified message. +// +// See https://msdn.microsoft.com/en-us/library/azure/hh452234.aspx +func (c QueueServiceClient) UpdateMessage(queue string, messageID string, message string, params UpdateMessageParameters) error { + uri := c.client.getEndpoint(queueServiceName, pathForMessage(queue, messageID), params.getParameters()) + req := putMessageRequest{MessageText: message} + body, nn, err := xmlMarshal(req) + if err != nil { + return err + } + headers := c.client.getStandardHeaders() + headers["Content-Length"] = fmt.Sprintf("%d", nn) + resp, err := c.client.exec(http.MethodPut, uri, headers, body, c.auth) + if err != nil { + return err + } + defer readAndCloseBody(resp.body) return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) } diff --git a/vendor/github.com/Azure/azure-storage-go/queueserviceclient.go b/vendor/github.com/Azure/azure-storage-go/queueserviceclient.go new file mode 100644 index 000000000..c26141339 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/queueserviceclient.go @@ -0,0 +1,20 @@ +package storage + +// QueueServiceClient contains operations for Microsoft Azure Queue Storage +// Service. +type QueueServiceClient struct { + client Client + auth authentication +} + +// GetServiceProperties gets the properties of your storage account's queue service. +// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-queue-service-properties +func (c *QueueServiceClient) GetServiceProperties() (*ServiceProperties, error) { + return c.client.getServiceProperties(queueServiceName, c.auth) +} + +// SetServiceProperties sets the properties of your storage account's queue service. +// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-queue-service-properties +func (c *QueueServiceClient) SetServiceProperties(props ServiceProperties) error { + return c.client.setServiceProperties(props, queueServiceName, c.auth) +} diff --git a/vendor/github.com/Azure/azure-storage-go/share.go b/vendor/github.com/Azure/azure-storage-go/share.go new file mode 100644 index 000000000..e190097ea --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/share.go @@ -0,0 +1,186 @@ +package storage + +import ( + "fmt" + "net/http" + "net/url" + "strconv" +) + +// Share represents an Azure file share. +type Share struct { + fsc *FileServiceClient + Name string `xml:"Name"` + Properties ShareProperties `xml:"Properties"` + Metadata map[string]string +} + +// ShareProperties contains various properties of a share. +type ShareProperties struct { + LastModified string `xml:"Last-Modified"` + Etag string `xml:"Etag"` + Quota int `xml:"Quota"` +} + +// builds the complete path for this share object. +func (s *Share) buildPath() string { + return fmt.Sprintf("/%s", s.Name) +} + +// Create this share under the associated account. +// If a share with the same name already exists, the operation fails. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn167008.aspx +func (s *Share) Create() error { + headers, err := s.fsc.createResource(s.buildPath(), resourceShare, nil, mergeMDIntoExtraHeaders(s.Metadata, nil), []int{http.StatusCreated}) + if err != nil { + return err + } + + s.updateEtagAndLastModified(headers) + return nil +} + +// CreateIfNotExists creates this share under the associated account if +// it does not exist. Returns true if the share is newly created or false if +// the share already exists. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn167008.aspx +func (s *Share) CreateIfNotExists() (bool, error) { + resp, err := s.fsc.createResourceNoClose(s.buildPath(), resourceShare, nil, nil) + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusCreated || resp.statusCode == http.StatusConflict { + if resp.statusCode == http.StatusCreated { + s.updateEtagAndLastModified(resp.headers) + return true, nil + } + return false, s.FetchAttributes() + } + } + + return false, err +} + +// Delete marks this share for deletion. The share along with any files +// and directories contained within it are later deleted during garbage +// collection. If the share does not exist the operation fails +// +// See https://msdn.microsoft.com/en-us/library/azure/dn689090.aspx +func (s *Share) Delete() error { + return s.fsc.deleteResource(s.buildPath(), resourceShare) +} + +// DeleteIfExists operation marks this share for deletion if it exists. +// +// See https://msdn.microsoft.com/en-us/library/azure/dn689090.aspx +func (s *Share) DeleteIfExists() (bool, error) { + resp, err := s.fsc.deleteResourceNoClose(s.buildPath(), resourceShare) + if resp != nil { + defer readAndCloseBody(resp.body) + if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound { + return resp.statusCode == http.StatusAccepted, nil + } + } + return false, err +} + +// Exists returns true if this share already exists +// on the storage account, otherwise returns false. +func (s *Share) Exists() (bool, error) { + exists, headers, err := s.fsc.resourceExists(s.buildPath(), resourceShare) + if exists { + s.updateEtagAndLastModified(headers) + s.updateQuota(headers) + } + return exists, err +} + +// FetchAttributes retrieves metadata and properties for this share. +func (s *Share) FetchAttributes() error { + headers, err := s.fsc.getResourceHeaders(s.buildPath(), compNone, resourceShare, http.MethodHead) + if err != nil { + return err + } + + s.updateEtagAndLastModified(headers) + s.updateQuota(headers) + s.Metadata = getMetadataFromHeaders(headers) + + return nil +} + +// GetRootDirectoryReference returns a Directory object at the root of this share. +func (s *Share) GetRootDirectoryReference() *Directory { + return &Directory{ + fsc: s.fsc, + share: s, + } +} + +// ServiceClient returns the FileServiceClient associated with this share. +func (s *Share) ServiceClient() *FileServiceClient { + return s.fsc +} + +// SetMetadata replaces the metadata for this share. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by GetShareMetadata. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx +func (s *Share) SetMetadata() error { + headers, err := s.fsc.setResourceHeaders(s.buildPath(), compMetadata, resourceShare, mergeMDIntoExtraHeaders(s.Metadata, nil)) + if err != nil { + return err + } + + s.updateEtagAndLastModified(headers) + return nil +} + +// SetProperties sets system properties for this share. +// +// Some keys may be converted to Camel-Case before sending. All keys +// are returned in lower case by SetShareProperties. HTTP header names +// are case-insensitive so case munging should not matter to other +// applications either. +// +// See https://msdn.microsoft.com/en-us/library/azure/mt427368.aspx +func (s *Share) SetProperties() error { + if s.Properties.Quota < 1 || s.Properties.Quota > 5120 { + return fmt.Errorf("invalid value %v for quota, valid values are [1, 5120]", s.Properties.Quota) + } + + headers, err := s.fsc.setResourceHeaders(s.buildPath(), compProperties, resourceShare, map[string]string{ + "x-ms-share-quota": strconv.Itoa(s.Properties.Quota), + }) + if err != nil { + return err + } + + s.updateEtagAndLastModified(headers) + return nil +} + +// updates Etag and last modified date +func (s *Share) updateEtagAndLastModified(headers http.Header) { + s.Properties.Etag = headers.Get("Etag") + s.Properties.LastModified = headers.Get("Last-Modified") +} + +// updates quota value +func (s *Share) updateQuota(headers http.Header) { + quota, err := strconv.Atoi(headers.Get("x-ms-share-quota")) + if err == nil { + s.Properties.Quota = quota + } +} + +// URL gets the canonical URL to this share. This method does not create a publicly accessible +// URL if the share is private and this method does not check if the share exists. +func (s *Share) URL() string { + return s.fsc.client.getEndpoint(fileServiceName, s.buildPath(), url.Values{}) +} diff --git a/vendor/github.com/Azure/azure-storage-go/storagepolicy.go b/vendor/github.com/Azure/azure-storage-go/storagepolicy.go new file mode 100644 index 000000000..bee1c31ad --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/storagepolicy.go @@ -0,0 +1,47 @@ +package storage + +import ( + "strings" + "time" +) + +// AccessPolicyDetailsXML has specifics about an access policy +// annotated with XML details. +type AccessPolicyDetailsXML struct { + StartTime time.Time `xml:"Start"` + ExpiryTime time.Time `xml:"Expiry"` + Permission string `xml:"Permission"` +} + +// SignedIdentifier is a wrapper for a specific policy +type SignedIdentifier struct { + ID string `xml:"Id"` + AccessPolicy AccessPolicyDetailsXML `xml:"AccessPolicy"` +} + +// SignedIdentifiers part of the response from GetPermissions call. +type SignedIdentifiers struct { + SignedIdentifiers []SignedIdentifier `xml:"SignedIdentifier"` +} + +// AccessPolicy is the response type from the GetPermissions call. +type AccessPolicy struct { + SignedIdentifiersList SignedIdentifiers `xml:"SignedIdentifiers"` +} + +// convertAccessPolicyToXMLStructs converts between AccessPolicyDetails which is a struct better for API usage to the +// AccessPolicy struct which will get converted to XML. +func convertAccessPolicyToXMLStructs(id string, startTime time.Time, expiryTime time.Time, permissions string) SignedIdentifier { + return SignedIdentifier{ + ID: id, + AccessPolicy: AccessPolicyDetailsXML{ + StartTime: startTime.UTC().Round(time.Second), + ExpiryTime: expiryTime.UTC().Round(time.Second), + Permission: permissions, + }, + } +} + +func updatePermissions(permissions, permission string) bool { + return strings.Contains(permissions, permission) +} diff --git a/vendor/github.com/Azure/azure-storage-go/storageservice.go b/vendor/github.com/Azure/azure-storage-go/storageservice.go new file mode 100644 index 000000000..817560b78 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/storageservice.go @@ -0,0 +1,118 @@ +package storage + +import ( + "fmt" + "net/http" + "net/url" +) + +// ServiceProperties represents the storage account service properties +type ServiceProperties struct { + Logging *Logging + HourMetrics *Metrics + MinuteMetrics *Metrics + Cors *Cors +} + +// Logging represents the Azure Analytics Logging settings +type Logging struct { + Version string + Delete bool + Read bool + Write bool + RetentionPolicy *RetentionPolicy +} + +// RetentionPolicy indicates if retention is enabled and for how many days +type RetentionPolicy struct { + Enabled bool + Days *int +} + +// Metrics provide request statistics. +type Metrics struct { + Version string + Enabled bool + IncludeAPIs *bool + RetentionPolicy *RetentionPolicy +} + +// Cors includes all the CORS rules +type Cors struct { + CorsRule []CorsRule +} + +// CorsRule includes all settings for a Cors rule +type CorsRule struct { + AllowedOrigins string + AllowedMethods string + MaxAgeInSeconds int + ExposedHeaders string + AllowedHeaders string +} + +func (c Client) getServiceProperties(service string, auth authentication) (*ServiceProperties, error) { + query := url.Values{ + "restype": {"service"}, + "comp": {"properties"}, + } + uri := c.getEndpoint(service, "", query) + headers := c.getStandardHeaders() + + resp, err := c.exec(http.MethodGet, uri, headers, nil, auth) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + return nil, err + } + + var out ServiceProperties + err = xmlUnmarshal(resp.body, &out) + if err != nil { + return nil, err + } + + return &out, nil +} + +func (c Client) setServiceProperties(props ServiceProperties, service string, auth authentication) error { + query := url.Values{ + "restype": {"service"}, + "comp": {"properties"}, + } + uri := c.getEndpoint(service, "", query) + + // Ideally, StorageServiceProperties would be the output struct + // This is to avoid golint stuttering, while generating the correct XML + type StorageServiceProperties struct { + Logging *Logging + HourMetrics *Metrics + MinuteMetrics *Metrics + Cors *Cors + } + input := StorageServiceProperties{ + Logging: props.Logging, + HourMetrics: props.HourMetrics, + MinuteMetrics: props.MinuteMetrics, + Cors: props.Cors, + } + + body, length, err := xmlMarshal(input) + if err != nil { + return err + } + + headers := c.getStandardHeaders() + headers["Content-Length"] = fmt.Sprintf("%v", length) + + resp, err := c.exec(http.MethodPut, uri, headers, body, auth) + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + + return checkRespCode(resp.statusCode, []int{http.StatusAccepted}) +} diff --git a/vendor/github.com/Azure/azure-storage-go/table.go b/vendor/github.com/Azure/azure-storage-go/table.go new file mode 100644 index 000000000..4123746e5 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/table.go @@ -0,0 +1,254 @@ +package storage + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "time" +) + +// AzureTable is the typedef of the Azure Table name +type AzureTable string + +const ( + tablesURIPath = "/Tables" +) + +type createTableRequest struct { + TableName string `json:"TableName"` +} + +// TableAccessPolicy are used for SETTING table policies +type TableAccessPolicy struct { + ID string + StartTime time.Time + ExpiryTime time.Time + CanRead bool + CanAppend bool + CanUpdate bool + CanDelete bool +} + +func pathForTable(table AzureTable) string { return fmt.Sprintf("%s", table) } + +func (c *TableServiceClient) getStandardHeaders() map[string]string { + return map[string]string{ + "x-ms-version": "2015-02-21", + "x-ms-date": currentTimeRfc1123Formatted(), + "Accept": "application/json;odata=nometadata", + "Accept-Charset": "UTF-8", + "Content-Type": "application/json", + userAgentHeader: c.client.userAgent, + } +} + +// QueryTables returns the tables created in the +// *TableServiceClient storage account. +func (c *TableServiceClient) QueryTables() ([]AzureTable, error) { + uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) + + headers := c.getStandardHeaders() + headers["Content-Length"] = "0" + + resp, err := c.client.execInternalJSON(http.MethodGet, uri, headers, nil, c.auth) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + ioutil.ReadAll(resp.body) + return nil, err + } + + buf := new(bytes.Buffer) + if _, err := buf.ReadFrom(resp.body); err != nil { + return nil, err + } + + var respArray queryTablesResponse + if err := json.Unmarshal(buf.Bytes(), &respArray); err != nil { + return nil, err + } + + s := make([]AzureTable, len(respArray.TableName)) + for i, elem := range respArray.TableName { + s[i] = AzureTable(elem.TableName) + } + + return s, nil +} + +// CreateTable creates the table given the specific +// name. This function fails if the name is not compliant +// with the specification or the tables already exists. +func (c *TableServiceClient) CreateTable(table AzureTable) error { + uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) + + headers := c.getStandardHeaders() + + req := createTableRequest{TableName: string(table)} + buf := new(bytes.Buffer) + + if err := json.NewEncoder(buf).Encode(req); err != nil { + return err + } + + headers["Content-Length"] = fmt.Sprintf("%d", buf.Len()) + + resp, err := c.client.execInternalJSON(http.MethodPost, uri, headers, buf, c.auth) + + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + + if err := checkRespCode(resp.statusCode, []int{http.StatusCreated}); err != nil { + return err + } + + return nil +} + +// DeleteTable deletes the table given the specific +// name. This function fails if the table is not present. +// Be advised: DeleteTable deletes all the entries +// that may be present. +func (c *TableServiceClient) DeleteTable(table AzureTable) error { + uri := c.client.getEndpoint(tableServiceName, tablesURIPath, url.Values{}) + uri += fmt.Sprintf("('%s')", string(table)) + + headers := c.getStandardHeaders() + + headers["Content-Length"] = "0" + + resp, err := c.client.execInternalJSON(http.MethodDelete, uri, headers, nil, c.auth) + + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + + if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil { + return err + + } + return nil +} + +// SetTablePermissions sets up table ACL permissions as per REST details https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Table-ACL +func (c *TableServiceClient) SetTablePermissions(table AzureTable, policies []TableAccessPolicy, timeout uint) (err error) { + params := url.Values{"comp": {"acl"}} + + if timeout > 0 { + params.Add("timeout", fmt.Sprint(timeout)) + } + + uri := c.client.getEndpoint(tableServiceName, string(table), params) + headers := c.client.getStandardHeaders() + + body, length, err := generateTableACLPayload(policies) + if err != nil { + return err + } + headers["Content-Length"] = fmt.Sprintf("%v", length) + + resp, err := c.client.execInternalJSON(http.MethodPut, uri, headers, body, c.auth) + if err != nil { + return err + } + defer readAndCloseBody(resp.body) + + if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil { + return err + } + return nil +} + +func generateTableACLPayload(policies []TableAccessPolicy) (io.Reader, int, error) { + sil := SignedIdentifiers{ + SignedIdentifiers: []SignedIdentifier{}, + } + for _, tap := range policies { + permission := generateTablePermissions(&tap) + signedIdentifier := convertAccessPolicyToXMLStructs(tap.ID, tap.StartTime, tap.ExpiryTime, permission) + sil.SignedIdentifiers = append(sil.SignedIdentifiers, signedIdentifier) + } + return xmlMarshal(sil) +} + +// GetTablePermissions gets the table ACL permissions, as per REST details https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-table-acl +func (c *TableServiceClient) GetTablePermissions(table AzureTable, timeout int) (permissionResponse []TableAccessPolicy, err error) { + params := url.Values{"comp": {"acl"}} + + if timeout > 0 { + params.Add("timeout", strconv.Itoa(timeout)) + } + + uri := c.client.getEndpoint(tableServiceName, string(table), params) + headers := c.client.getStandardHeaders() + resp, err := c.client.execInternalJSON(http.MethodGet, uri, headers, nil, c.auth) + if err != nil { + return nil, err + } + defer resp.body.Close() + + if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + ioutil.ReadAll(resp.body) + return nil, err + } + + var ap AccessPolicy + err = xmlUnmarshal(resp.body, &ap.SignedIdentifiersList) + if err != nil { + return nil, err + } + out := updateTableAccessPolicy(ap) + return out, nil +} + +func updateTableAccessPolicy(ap AccessPolicy) []TableAccessPolicy { + out := []TableAccessPolicy{} + for _, policy := range ap.SignedIdentifiersList.SignedIdentifiers { + tap := TableAccessPolicy{ + ID: policy.ID, + StartTime: policy.AccessPolicy.StartTime, + ExpiryTime: policy.AccessPolicy.ExpiryTime, + } + tap.CanRead = updatePermissions(policy.AccessPolicy.Permission, "r") + tap.CanAppend = updatePermissions(policy.AccessPolicy.Permission, "a") + tap.CanUpdate = updatePermissions(policy.AccessPolicy.Permission, "u") + tap.CanDelete = updatePermissions(policy.AccessPolicy.Permission, "d") + + out = append(out, tap) + } + return out +} + +func generateTablePermissions(tap *TableAccessPolicy) (permissions string) { + // generate the permissions string (raud). + // still want the end user API to have bool flags. + permissions = "" + + if tap.CanRead { + permissions += "r" + } + + if tap.CanAppend { + permissions += "a" + } + + if tap.CanUpdate { + permissions += "u" + } + + if tap.CanDelete { + permissions += "d" + } + return permissions +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/table_entities.go b/vendor/github.com/Azure/azure-storage-go/table_entities.go index 1b5919cd1..36413a0cf 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/storage/table_entities.go +++ b/vendor/github.com/Azure/azure-storage-go/table_entities.go @@ -10,6 +10,8 @@ import ( "reflect" ) +// Annotating as secure for gas scanning +/* #nosec */ const ( partitionKeyNode = "PartitionKey" rowKeyNode = "RowKey" @@ -96,7 +98,7 @@ func (c *TableServiceClient) QueryTableEntities(tableName AzureTable, previousCo headers["Content-Length"] = "0" - resp, err := c.client.execTable("GET", uri, headers, nil) + resp, err := c.client.execInternalJSON(http.MethodGet, uri, headers, nil, c.auth) if err != nil { return nil, nil, err @@ -104,12 +106,9 @@ func (c *TableServiceClient) QueryTableEntities(tableName AzureTable, previousCo contToken := extractContinuationTokenFromHeaders(resp.headers) - if err != nil { - return nil, contToken, err - } defer resp.body.Close() - if err := checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { + if err = checkRespCode(resp.statusCode, []int{http.StatusOK}); err != nil { return nil, contToken, err } @@ -125,13 +124,12 @@ func (c *TableServiceClient) QueryTableEntities(tableName AzureTable, previousCo // The function fails if there is an entity with the same // PartitionKey and RowKey in the table. func (c *TableServiceClient) InsertEntity(table AzureTable, entity TableEntity) error { - var err error - - if sc, err := c.execTable(table, entity, false, "POST"); err != nil { - return checkRespCode(sc, []int{http.StatusCreated}) + sc, err := c.execTable(table, entity, false, http.MethodPost) + if err != nil { + return err } - return err + return checkRespCode(sc, []int{http.StatusCreated}) } func (c *TableServiceClient) execTable(table AzureTable, entity TableEntity, specifyKeysInURL bool, method string) (int, error) { @@ -150,10 +148,7 @@ func (c *TableServiceClient) execTable(table AzureTable, entity TableEntity, spe headers["Content-Length"] = fmt.Sprintf("%d", buf.Len()) - var err error - var resp *odataResponse - - resp, err = c.client.execTable(method, uri, headers, &buf) + resp, err := c.client.execInternalJSON(method, uri, headers, &buf, c.auth) if err != nil { return 0, err @@ -168,12 +163,12 @@ func (c *TableServiceClient) execTable(table AzureTable, entity TableEntity, spe // one passed as parameter. The function fails if there is no entity // with the same PartitionKey and RowKey in the table. func (c *TableServiceClient) UpdateEntity(table AzureTable, entity TableEntity) error { - var err error - - if sc, err := c.execTable(table, entity, true, "PUT"); err != nil { - return checkRespCode(sc, []int{http.StatusNoContent}) + sc, err := c.execTable(table, entity, true, http.MethodPut) + if err != nil { + return err } - return err + + return checkRespCode(sc, []int{http.StatusNoContent}) } // MergeEntity merges the contents of an entity with the @@ -181,12 +176,12 @@ func (c *TableServiceClient) UpdateEntity(table AzureTable, entity TableEntity) // The function fails if there is no entity // with the same PartitionKey and RowKey in the table. func (c *TableServiceClient) MergeEntity(table AzureTable, entity TableEntity) error { - var err error - - if sc, err := c.execTable(table, entity, true, "MERGE"); err != nil { - return checkRespCode(sc, []int{http.StatusNoContent}) + sc, err := c.execTable(table, entity, true, "MERGE") + if err != nil { + return err } - return err + + return checkRespCode(sc, []int{http.StatusNoContent}) } // DeleteEntityWithoutCheck deletes the entity matching by @@ -212,7 +207,7 @@ func (c *TableServiceClient) DeleteEntity(table AzureTable, entity TableEntity, headers["Content-Length"] = "0" headers["If-Match"] = ifMatch - resp, err := c.client.execTable("DELETE", uri, headers, nil) + resp, err := c.client.execInternalJSON(http.MethodDelete, uri, headers, nil, c.auth) if err != nil { return err @@ -229,23 +224,23 @@ func (c *TableServiceClient) DeleteEntity(table AzureTable, entity TableEntity, // InsertOrReplaceEntity inserts an entity in the specified table // or replaced the existing one. func (c *TableServiceClient) InsertOrReplaceEntity(table AzureTable, entity TableEntity) error { - var err error - - if sc, err := c.execTable(table, entity, true, "PUT"); err != nil { - return checkRespCode(sc, []int{http.StatusNoContent}) + sc, err := c.execTable(table, entity, true, http.MethodPut) + if err != nil { + return err } - return err + + return checkRespCode(sc, []int{http.StatusNoContent}) } // InsertOrMergeEntity inserts an entity in the specified table // or merges the existing one. func (c *TableServiceClient) InsertOrMergeEntity(table AzureTable, entity TableEntity) error { - var err error - - if sc, err := c.execTable(table, entity, true, "MERGE"); err != nil { - return checkRespCode(sc, []int{http.StatusNoContent}) + sc, err := c.execTable(table, entity, true, "MERGE") + if err != nil { + return err } - return err + + return checkRespCode(sc, []int{http.StatusNoContent}) } func injectPartitionAndRowKeys(entity TableEntity, buf *bytes.Buffer) error { @@ -338,8 +333,12 @@ func deserializeEntity(retType reflect.Type, reader io.Reader) ([]TableEntity, e } // Reset PartitionKey and RowKey - tEntries[i].SetPartitionKey(pKey) - tEntries[i].SetRowKey(rKey) + if err := tEntries[i].SetPartitionKey(pKey); err != nil { + return nil, err + } + if err := tEntries[i].SetRowKey(rKey); err != nil { + return nil, err + } } return tEntries, nil diff --git a/vendor/github.com/Azure/azure-storage-go/tableserviceclient.go b/vendor/github.com/Azure/azure-storage-go/tableserviceclient.go new file mode 100644 index 000000000..ee5e0a867 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/tableserviceclient.go @@ -0,0 +1,20 @@ +package storage + +// TableServiceClient contains operations for Microsoft Azure Table Storage +// Service. +type TableServiceClient struct { + client Client + auth authentication +} + +// GetServiceProperties gets the properties of your storage account's table service. +// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-table-service-properties +func (c *TableServiceClient) GetServiceProperties() (*ServiceProperties, error) { + return c.client.getServiceProperties(tableServiceName, c.auth) +} + +// SetServiceProperties sets the properties of your storage account's table service. +// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-table-service-properties +func (c *TableServiceClient) SetServiceProperties(props ServiceProperties) error { + return c.client.setServiceProperties(props, tableServiceName, c.auth) +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go b/vendor/github.com/Azure/azure-storage-go/util.go index d71c6ce55..57ca1b6d9 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/storage/util.go +++ b/vendor/github.com/Azure/azure-storage-go/util.go @@ -77,7 +77,7 @@ func headersFromStruct(v interface{}) map[string]string { for i := 0; i < value.NumField(); i++ { key := value.Type().Field(i).Tag.Get("header") val := value.Field(i).String() - if val != "" { + if key != "" && val != "" { headers[key] = val } } diff --git a/vendor/github.com/Azure/azure-storage-go/version.go b/vendor/github.com/Azure/azure-storage-go/version.go new file mode 100644 index 000000000..c25fe3371 --- /dev/null +++ b/vendor/github.com/Azure/azure-storage-go/version.go @@ -0,0 +1,5 @@ +package storage + +var ( + sdkVersion = "0.1.0" +) diff --git a/vendor/github.com/Azure/go-autorest/LICENSE b/vendor/github.com/Azure/go-autorest/LICENSE new file mode 100644 index 000000000..b9d6a27ea --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015 Microsoft Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/Azure/go-autorest/autorest/autorest.go b/vendor/github.com/Azure/go-autorest/autorest/autorest.go new file mode 100644 index 000000000..51f1c4bbc --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/autorest.go @@ -0,0 +1,115 @@ +/* +Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines +and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/) +generated Go code. + +The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending, +and Responding. A typical pattern is: + + req, err := Prepare(&http.Request{}, + token.WithAuthorization()) + + resp, err := Send(req, + WithLogging(logger), + DoErrorIfStatusCode(http.StatusInternalServerError), + DoCloseIfError(), + DoRetryForAttempts(5, time.Second)) + + err = Respond(resp, + ByDiscardingBody(), + ByClosing()) + +Each phase relies on decorators to modify and / or manage processing. Decorators may first modify +and then pass the data along, pass the data first and then modify the result, or wrap themselves +around passing the data (such as a logger might do). Decorators run in the order provided. For +example, the following: + + req, err := Prepare(&http.Request{}, + WithBaseURL("https://microsoft.com/"), + WithPath("a"), + WithPath("b"), + WithPath("c")) + +will set the URL to: + + https://microsoft.com/a/b/c + +Preparers and Responders may be shared and re-used (assuming the underlying decorators support +sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders +shared among multiple go-routines, and a single Sender shared among multiple sending go-routines, +all bound together by means of input / output channels. + +Decorators hold their passed state within a closure (such as the path components in the example +above). Be careful to share Preparers and Responders only in a context where such held state +applies. For example, it may not make sense to share a Preparer that applies a query string from a +fixed set of values. Similarly, sharing a Responder that reads the response body into a passed +struct (e.g., ByUnmarshallingJson) is likely incorrect. + +Lastly, the Swagger specification (https://swagger.io) that drives AutoRest +(https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The +github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure +correct parsing and formatting. + +Errors raised by autorest objects and methods will conform to the autorest.Error interface. + +See the included examples for more detail. For details on the suggested use of this package by +generated clients, see the Client described below. +*/ +package autorest + +import ( + "net/http" + "time" +) + +const ( + // HeaderLocation specifies the HTTP Location header. + HeaderLocation = "Location" + + // HeaderRetryAfter specifies the HTTP Retry-After header. + HeaderRetryAfter = "Retry-After" +) + +// ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set +// and false otherwise. +func ResponseHasStatusCode(resp *http.Response, codes ...int) bool { + return containsInt(codes, resp.StatusCode) +} + +// GetLocation retrieves the URL from the Location header of the passed response. +func GetLocation(resp *http.Response) string { + return resp.Header.Get(HeaderLocation) +} + +// GetRetryAfter extracts the retry delay from the Retry-After header of the passed response. If +// the header is absent or is malformed, it will return the supplied default delay time.Duration. +func GetRetryAfter(resp *http.Response, defaultDelay time.Duration) time.Duration { + retry := resp.Header.Get(HeaderRetryAfter) + if retry == "" { + return defaultDelay + } + + d, err := time.ParseDuration(retry + "s") + if err != nil { + return defaultDelay + } + + return d +} + +// NewPollingRequest allocates and returns a new http.Request to poll for the passed response. +func NewPollingRequest(resp *http.Response, cancel <-chan struct{}) (*http.Request, error) { + location := GetLocation(resp) + if location == "" { + return nil, NewErrorWithResponse("autorest", "NewPollingRequest", resp, "Location header missing from response that requires polling") + } + + req, err := Prepare(&http.Request{Cancel: cancel}, + AsGet(), + WithBaseURL(location)) + if err != nil { + return nil, NewErrorWithError(err, "autorest", "NewPollingRequest", nil, "Failure creating poll request to %s", location) + } + + return req, nil +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go new file mode 100644 index 000000000..6e076981f --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go @@ -0,0 +1,308 @@ +package azure + +import ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/date" +) + +const ( + headerAsyncOperation = "Azure-AsyncOperation" +) + +const ( + methodDelete = "DELETE" + methodPatch = "PATCH" + methodPost = "POST" + methodPut = "PUT" + methodGet = "GET" + + operationInProgress string = "InProgress" + operationCanceled string = "Canceled" + operationFailed string = "Failed" + operationSucceeded string = "Succeeded" +) + +// DoPollForAsynchronous returns a SendDecorator that polls if the http.Response is for an Azure +// long-running operation. It will delay between requests for the duration specified in the +// RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by +// closing the optional channel on the http.Request. +func DoPollForAsynchronous(delay time.Duration) autorest.SendDecorator { + return func(s autorest.Sender) autorest.Sender { + return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) { + resp, err = s.Do(r) + if err != nil { + return resp, err + } + pollingCodes := []int{http.StatusAccepted, http.StatusCreated, http.StatusOK} + if !autorest.ResponseHasStatusCode(resp, pollingCodes...) { + return resp, nil + } + + ps := pollingState{} + for err == nil { + err = updatePollingState(resp, &ps) + if err != nil { + break + } + if ps.hasTerminated() { + if !ps.hasSucceeded() { + err = ps + } + break + } + + r, err = newPollingRequest(resp, ps) + if err != nil { + return resp, err + } + + delay = autorest.GetRetryAfter(resp, delay) + resp, err = autorest.SendWithSender(s, r, + autorest.AfterDelay(delay)) + } + + return resp, err + }) + } +} + +func getAsyncOperation(resp *http.Response) string { + return resp.Header.Get(http.CanonicalHeaderKey(headerAsyncOperation)) +} + +func hasSucceeded(state string) bool { + return state == operationSucceeded +} + +func hasTerminated(state string) bool { + switch state { + case operationCanceled, operationFailed, operationSucceeded: + return true + default: + return false + } +} + +func hasFailed(state string) bool { + return state == operationFailed +} + +type provisioningTracker interface { + state() string + hasSucceeded() bool + hasTerminated() bool +} + +type operationResource struct { + // Note: + // The specification states services should return the "id" field. However some return it as + // "operationId". + ID string `json:"id"` + OperationID string `json:"operationId"` + Name string `json:"name"` + Status string `json:"status"` + Properties map[string]interface{} `json:"properties"` + OperationError ServiceError `json:"error"` + StartTime date.Time `json:"startTime"` + EndTime date.Time `json:"endTime"` + PercentComplete float64 `json:"percentComplete"` +} + +func (or operationResource) state() string { + return or.Status +} + +func (or operationResource) hasSucceeded() bool { + return hasSucceeded(or.state()) +} + +func (or operationResource) hasTerminated() bool { + return hasTerminated(or.state()) +} + +type provisioningProperties struct { + ProvisioningState string `json:"provisioningState"` +} + +type provisioningStatus struct { + Properties provisioningProperties `json:"properties,omitempty"` + ProvisioningError ServiceError `json:"error,omitempty"` +} + +func (ps provisioningStatus) state() string { + return ps.Properties.ProvisioningState +} + +func (ps provisioningStatus) hasSucceeded() bool { + return hasSucceeded(ps.state()) +} + +func (ps provisioningStatus) hasTerminated() bool { + return hasTerminated(ps.state()) +} + +func (ps provisioningStatus) hasProvisioningError() bool { + return ps.ProvisioningError != ServiceError{} +} + +type pollingResponseFormat string + +const ( + usesOperationResponse pollingResponseFormat = "OperationResponse" + usesProvisioningStatus pollingResponseFormat = "ProvisioningStatus" + formatIsUnknown pollingResponseFormat = "" +) + +type pollingState struct { + responseFormat pollingResponseFormat + uri string + state string + code string + message string +} + +func (ps pollingState) hasSucceeded() bool { + return hasSucceeded(ps.state) +} + +func (ps pollingState) hasTerminated() bool { + return hasTerminated(ps.state) +} + +func (ps pollingState) hasFailed() bool { + return hasFailed(ps.state) +} + +func (ps pollingState) Error() string { + return fmt.Sprintf("Long running operation terminated with status '%s': Code=%q Message=%q", ps.state, ps.code, ps.message) +} + +// updatePollingState maps the operation status -- retrieved from either a provisioningState +// field, the status field of an OperationResource, or inferred from the HTTP status code -- +// into a well-known states. Since the process begins from the initial request, the state +// always comes from either a the provisioningState returned or is inferred from the HTTP +// status code. Subsequent requests will read an Azure OperationResource object if the +// service initially returned the Azure-AsyncOperation header. The responseFormat field notes +// the expected response format. +func updatePollingState(resp *http.Response, ps *pollingState) error { + // Determine the response shape + // -- The first response will always be a provisioningStatus response; only the polling requests, + // depending on the header returned, may be something otherwise. + var pt provisioningTracker + if ps.responseFormat == usesOperationResponse { + pt = &operationResource{} + } else { + pt = &provisioningStatus{} + } + + // If this is the first request (that is, the polling response shape is unknown), determine how + // to poll and what to expect + if ps.responseFormat == formatIsUnknown { + req := resp.Request + if req == nil { + return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Original HTTP request is missing") + } + + // Prefer the Azure-AsyncOperation header + ps.uri = getAsyncOperation(resp) + if ps.uri != "" { + ps.responseFormat = usesOperationResponse + } else { + ps.responseFormat = usesProvisioningStatus + } + + // Else, use the Location header + if ps.uri == "" { + ps.uri = autorest.GetLocation(resp) + } + + // Lastly, requests against an existing resource, use the last request URI + if ps.uri == "" { + m := strings.ToUpper(req.Method) + if m == methodPatch || m == methodPut || m == methodGet { + ps.uri = req.URL.String() + } + } + } + + // Read and interpret the response (saving the Body in case no polling is necessary) + b := &bytes.Buffer{} + err := autorest.Respond(resp, + autorest.ByCopying(b), + autorest.ByUnmarshallingJSON(pt), + autorest.ByClosing()) + resp.Body = ioutil.NopCloser(b) + if err != nil { + return err + } + + // Interpret the results + // -- Terminal states apply regardless + // -- Unknown states are per-service inprogress states + // -- Otherwise, infer state from HTTP status code + if pt.hasTerminated() { + ps.state = pt.state() + } else if pt.state() != "" { + ps.state = operationInProgress + } else { + switch resp.StatusCode { + case http.StatusAccepted: + ps.state = operationInProgress + + case http.StatusNoContent, http.StatusCreated, http.StatusOK: + ps.state = operationSucceeded + + default: + ps.state = operationFailed + } + } + + if ps.state == operationInProgress && ps.uri == "" { + return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Unable to obtain polling URI for %s %s", resp.Request.Method, resp.Request.URL) + } + + // For failed operation, check for error code and message in + // -- Operation resource + // -- Response + // -- Otherwise, Unknown + if ps.hasFailed() { + if ps.responseFormat == usesOperationResponse { + or := pt.(*operationResource) + ps.code = or.OperationError.Code + ps.message = or.OperationError.Message + } else { + p := pt.(*provisioningStatus) + if p.hasProvisioningError() { + ps.code = p.ProvisioningError.Code + ps.message = p.ProvisioningError.Message + } else { + ps.code = "Unknown" + ps.message = "None" + } + } + } + return nil +} + +func newPollingRequest(resp *http.Response, ps pollingState) (*http.Request, error) { + req := resp.Request + if req == nil { + return nil, autorest.NewError("azure", "newPollingRequest", "Azure Polling Error - Original HTTP request is missing") + } + + reqPoll, err := autorest.Prepare(&http.Request{Cancel: req.Cancel}, + autorest.AsGet(), + autorest.WithBaseURL(ps.uri)) + if err != nil { + return nil, autorest.NewErrorWithError(err, "azure", "newPollingRequest", nil, "Failure creating poll request to %s", ps.uri) + } + + return reqPoll, nil +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go b/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go new file mode 100644 index 000000000..3f4d13421 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go @@ -0,0 +1,180 @@ +/* +Package azure provides Azure-specific implementations used with AutoRest. + +See the included examples for more detail. +*/ +package azure + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strconv" + + "github.com/Azure/go-autorest/autorest" +) + +const ( + // HeaderClientID is the Azure extension header to set a user-specified request ID. + HeaderClientID = "x-ms-client-request-id" + + // HeaderReturnClientID is the Azure extension header to set if the user-specified request ID + // should be included in the response. + HeaderReturnClientID = "x-ms-return-client-request-id" + + // HeaderRequestID is the Azure extension header of the service generated request ID returned + // in the response. + HeaderRequestID = "x-ms-request-id" +) + +// ServiceError encapsulates the error response from an Azure service. +type ServiceError struct { + Code string `json:"code"` + Message string `json:"message"` + Details *[]interface{} `json:"details"` +} + +func (se ServiceError) Error() string { + if se.Details != nil { + d, err := json.Marshal(*(se.Details)) + if err != nil { + return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, *se.Details) + } + return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, string(d)) + } + return fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message) +} + +// RequestError describes an error response returned by Azure service. +type RequestError struct { + autorest.DetailedError + + // The error returned by the Azure service. + ServiceError *ServiceError `json:"error"` + + // The request id (from the x-ms-request-id-header) of the request. + RequestID string +} + +// Error returns a human-friendly error message from service error. +func (e RequestError) Error() string { + return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v", + e.StatusCode, e.ServiceError) +} + +// IsAzureError returns true if the passed error is an Azure Service error; false otherwise. +func IsAzureError(e error) bool { + _, ok := e.(*RequestError) + return ok +} + +// NewErrorWithError creates a new Error conforming object from the +// passed packageType, method, statusCode of the given resp (UndefinedStatusCode +// if resp is nil), message, and original error. message is treated as a format +// string to which the optional args apply. +func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError { + if v, ok := original.(*RequestError); ok { + return *v + } + + statusCode := autorest.UndefinedStatusCode + if resp != nil { + statusCode = resp.StatusCode + } + return RequestError{ + DetailedError: autorest.DetailedError{ + Original: original, + PackageType: packageType, + Method: method, + StatusCode: statusCode, + Message: fmt.Sprintf(message, args...), + }, + } +} + +// WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of +// x-ms-client-request-id whose value is the passed, undecorated UUID (e.g., +// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id +// header to true such that UUID accompanies the http.Response. +func WithReturningClientID(uuid string) autorest.PrepareDecorator { + preparer := autorest.CreatePreparer( + WithClientID(uuid), + WithReturnClientID(true)) + + return func(p autorest.Preparer) autorest.Preparer { + return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err != nil { + return r, err + } + return preparer.Prepare(r) + }) + } +} + +// WithClientID returns a PrepareDecorator that adds an HTTP extension header of +// x-ms-client-request-id whose value is passed, undecorated UUID (e.g., +// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). +func WithClientID(uuid string) autorest.PrepareDecorator { + return autorest.WithHeader(HeaderClientID, uuid) +} + +// WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of +// x-ms-return-client-request-id whose boolean value indicates if the value of the +// x-ms-client-request-id header should be included in the http.Response. +func WithReturnClientID(b bool) autorest.PrepareDecorator { + return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b)) +} + +// ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the +// http.Request sent to the service (and returned in the http.Response) +func ExtractClientID(resp *http.Response) string { + return autorest.ExtractHeaderValue(HeaderClientID, resp) +} + +// ExtractRequestID extracts the Azure server generated request identifier from the +// x-ms-request-id header. +func ExtractRequestID(resp *http.Response) string { + return autorest.ExtractHeaderValue(HeaderRequestID, resp) +} + +// WithErrorUnlessStatusCode returns a RespondDecorator that emits an +// azure.RequestError by reading the response body unless the response HTTP status code +// is among the set passed. +// +// If there is a chance service may return responses other than the Azure error +// format and the response cannot be parsed into an error, a decoding error will +// be returned containing the response body. In any case, the Responder will +// return an error if the status code is not satisfied. +// +// If this Responder returns an error, the response body will be replaced with +// an in-memory reader, which needs no further closing. +func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator { + return func(r autorest.Responder) autorest.Responder { + return autorest.ResponderFunc(func(resp *http.Response) error { + err := r.Respond(resp) + if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) { + var e RequestError + defer resp.Body.Close() + + // Copy and replace the Body in case it does not contain an error object. + // This will leave the Body available to the caller. + b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e) + resp.Body = ioutil.NopCloser(&b) + if decodeErr != nil { + return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr) + } else if e.ServiceError == nil { + e.ServiceError = &ServiceError{Code: "Unknown", Message: "Unknown service error"} + } + + e.RequestID = ExtractRequestID(resp) + if e.StatusCode == nil { + e.StatusCode = resp.StatusCode + } + err = &e + } + return err + }) + } +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/config.go b/vendor/github.com/Azure/go-autorest/autorest/azure/config.go new file mode 100644 index 000000000..bea30b0d6 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/config.go @@ -0,0 +1,13 @@ +package azure + +import ( + "net/url" +) + +// OAuthConfig represents the endpoints needed +// in OAuth operations +type OAuthConfig struct { + AuthorizeEndpoint url.URL + TokenEndpoint url.URL + DeviceCodeEndpoint url.URL +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/devicetoken.go b/vendor/github.com/Azure/go-autorest/autorest/azure/devicetoken.go new file mode 100644 index 000000000..e1d5498a8 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/devicetoken.go @@ -0,0 +1,193 @@ +package azure + +/* + This file is largely based on rjw57/oauth2device's code, with the follow differences: + * scope -> resource, and only allow a single one + * receive "Message" in the DeviceCode struct and show it to users as the prompt + * azure-xplat-cli has the following behavior that this emulates: + - does not send client_secret during the token exchange + - sends resource again in the token exchange request +*/ + +import ( + "fmt" + "net/http" + "net/url" + "time" + + "github.com/Azure/go-autorest/autorest" +) + +const ( + logPrefix = "autorest/azure/devicetoken:" +) + +var ( + // ErrDeviceGeneric represents an unknown error from the token endpoint when using device flow + ErrDeviceGeneric = fmt.Errorf("%s Error while retrieving OAuth token: Unknown Error", logPrefix) + + // ErrDeviceAccessDenied represents an access denied error from the token endpoint when using device flow + ErrDeviceAccessDenied = fmt.Errorf("%s Error while retrieving OAuth token: Access Denied", logPrefix) + + // ErrDeviceAuthorizationPending represents the server waiting on the user to complete the device flow + ErrDeviceAuthorizationPending = fmt.Errorf("%s Error while retrieving OAuth token: Authorization Pending", logPrefix) + + // ErrDeviceCodeExpired represents the server timing out and expiring the code during device flow + ErrDeviceCodeExpired = fmt.Errorf("%s Error while retrieving OAuth token: Code Expired", logPrefix) + + // ErrDeviceSlowDown represents the service telling us we're polling too often during device flow + ErrDeviceSlowDown = fmt.Errorf("%s Error while retrieving OAuth token: Slow Down", logPrefix) + + errCodeSendingFails = "Error occurred while sending request for Device Authorization Code" + errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint" + errTokenSendingFails = "Error occurred while sending request with device code for a token" + errTokenHandlingFails = "Error occurred while handling response from the Token Endpoint (during device flow)" +) + +// DeviceCode is the object returned by the device auth endpoint +// It contains information to instruct the user to complete the auth flow +type DeviceCode struct { + DeviceCode *string `json:"device_code,omitempty"` + UserCode *string `json:"user_code,omitempty"` + VerificationURL *string `json:"verification_url,omitempty"` + ExpiresIn *int64 `json:"expires_in,string,omitempty"` + Interval *int64 `json:"interval,string,omitempty"` + + Message *string `json:"message"` // Azure specific + Resource string // store the following, stored when initiating, used when exchanging + OAuthConfig OAuthConfig + ClientID string +} + +// TokenError is the object returned by the token exchange endpoint +// when something is amiss +type TokenError struct { + Error *string `json:"error,omitempty"` + ErrorCodes []int `json:"error_codes,omitempty"` + ErrorDescription *string `json:"error_description,omitempty"` + Timestamp *string `json:"timestamp,omitempty"` + TraceID *string `json:"trace_id,omitempty"` +} + +// DeviceToken is the object return by the token exchange endpoint +// It can either look like a Token or an ErrorToken, so put both here +// and check for presence of "Error" to know if we are in error state +type deviceToken struct { + Token + TokenError +} + +// InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode +// that can be used with CheckForUserCompletion or WaitForUserCompletion. +func InitiateDeviceAuth(client *autorest.Client, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) { + req, _ := autorest.Prepare( + &http.Request{}, + autorest.AsPost(), + autorest.AsFormURLEncoded(), + autorest.WithBaseURL(oauthConfig.DeviceCodeEndpoint.String()), + autorest.WithFormData(url.Values{ + "client_id": []string{clientID}, + "resource": []string{resource}, + }), + ) + + resp, err := autorest.SendWithSender(client, req) + if err != nil { + return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err) + } + + var code DeviceCode + err = autorest.Respond( + resp, + autorest.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&code), + autorest.ByClosing()) + if err != nil { + return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err) + } + + code.ClientID = clientID + code.Resource = resource + code.OAuthConfig = oauthConfig + + return &code, nil +} + +// CheckForUserCompletion takes a DeviceCode and checks with the Azure AD OAuth endpoint +// to see if the device flow has: been completed, timed out, or otherwise failed +func CheckForUserCompletion(client *autorest.Client, code *DeviceCode) (*Token, error) { + req, _ := autorest.Prepare( + &http.Request{}, + autorest.AsPost(), + autorest.AsFormURLEncoded(), + autorest.WithBaseURL(code.OAuthConfig.TokenEndpoint.String()), + autorest.WithFormData(url.Values{ + "client_id": []string{code.ClientID}, + "code": []string{*code.DeviceCode}, + "grant_type": []string{OAuthGrantTypeDeviceCode}, + "resource": []string{code.Resource}, + }), + ) + + resp, err := autorest.SendWithSender(client, req) + if err != nil { + return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err) + } + + var token deviceToken + err = autorest.Respond( + resp, + autorest.WithErrorUnlessStatusCode(http.StatusOK, http.StatusBadRequest), + autorest.ByUnmarshallingJSON(&token), + autorest.ByClosing()) + if err != nil { + return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err) + } + + if token.Error == nil { + return &token.Token, nil + } + + switch *token.Error { + case "authorization_pending": + return nil, ErrDeviceAuthorizationPending + case "slow_down": + return nil, ErrDeviceSlowDown + case "access_denied": + return nil, ErrDeviceAccessDenied + case "code_expired": + return nil, ErrDeviceCodeExpired + default: + return nil, ErrDeviceGeneric + } +} + +// WaitForUserCompletion calls CheckForUserCompletion repeatedly until a token is granted or an error state occurs. +// This prevents the user from looping and checking against 'ErrDeviceAuthorizationPending'. +func WaitForUserCompletion(client *autorest.Client, code *DeviceCode) (*Token, error) { + intervalDuration := time.Duration(*code.Interval) * time.Second + waitDuration := intervalDuration + + for { + token, err := CheckForUserCompletion(client, code) + + if err == nil { + return token, nil + } + + switch err { + case ErrDeviceSlowDown: + waitDuration += waitDuration + case ErrDeviceAuthorizationPending: + // noop + default: // everything else is "fatal" to us + return nil, err + } + + if waitDuration > (intervalDuration * 3) { + return nil, fmt.Errorf("%s Error waiting for user to complete device flow. Server told us to slow_down too much", logPrefix) + } + + time.Sleep(waitDuration) + } +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go b/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go new file mode 100644 index 000000000..4701b4376 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go @@ -0,0 +1,167 @@ +package azure + +import ( + "fmt" + "net/url" + "strings" +) + +const ( + activeDirectoryAPIVersion = "1.0" +) + +var environments = map[string]Environment{ + "AZURECHINACLOUD": ChinaCloud, + "AZUREGERMANCLOUD": GermanCloud, + "AZUREPUBLICCLOUD": PublicCloud, + "AZUREUSGOVERNMENTCLOUD": USGovernmentCloud, +} + +// Environment represents a set of endpoints for each of Azure's Clouds. +type Environment struct { + Name string `json:"name"` + ManagementPortalURL string `json:"managementPortalURL"` + PublishSettingsURL string `json:"publishSettingsURL"` + ServiceManagementEndpoint string `json:"serviceManagementEndpoint"` + ResourceManagerEndpoint string `json:"resourceManagerEndpoint"` + ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"` + GalleryEndpoint string `json:"galleryEndpoint"` + KeyVaultEndpoint string `json:"keyVaultEndpoint"` + GraphEndpoint string `json:"graphEndpoint"` + StorageEndpointSuffix string `json:"storageEndpointSuffix"` + SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"` + TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"` + KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"` + ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"` + ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"` + ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"` + ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"` +} + +var ( + // PublicCloud is the default public Azure cloud environment + PublicCloud = Environment{ + Name: "AzurePublicCloud", + ManagementPortalURL: "https://manage.windowsazure.com/", + PublishSettingsURL: "https://manage.windowsazure.com/publishsettings/index", + ServiceManagementEndpoint: "https://management.core.windows.net/", + ResourceManagerEndpoint: "https://management.azure.com/", + ActiveDirectoryEndpoint: "https://login.microsoftonline.com/", + GalleryEndpoint: "https://gallery.azure.com/", + KeyVaultEndpoint: "https://vault.azure.net/", + GraphEndpoint: "https://graph.windows.net/", + StorageEndpointSuffix: "core.windows.net", + SQLDatabaseDNSSuffix: "database.windows.net", + TrafficManagerDNSSuffix: "trafficmanager.net", + KeyVaultDNSSuffix: "vault.azure.net", + ServiceBusEndpointSuffix: "servicebus.azure.com", + ServiceManagementVMDNSSuffix: "cloudapp.net", + ResourceManagerVMDNSSuffix: "cloudapp.azure.com", + ContainerRegistryDNSSuffix: "azurecr.io", + } + + // USGovernmentCloud is the cloud environment for the US Government + USGovernmentCloud = Environment{ + Name: "AzureUSGovernmentCloud", + ManagementPortalURL: "https://manage.windowsazure.us/", + PublishSettingsURL: "https://manage.windowsazure.us/publishsettings/index", + ServiceManagementEndpoint: "https://management.core.usgovcloudapi.net/", + ResourceManagerEndpoint: "https://management.usgovcloudapi.net/", + ActiveDirectoryEndpoint: "https://login.microsoftonline.com/", + GalleryEndpoint: "https://gallery.usgovcloudapi.net/", + KeyVaultEndpoint: "https://vault.usgovcloudapi.net/", + GraphEndpoint: "https://graph.usgovcloudapi.net/", + StorageEndpointSuffix: "core.usgovcloudapi.net", + SQLDatabaseDNSSuffix: "database.usgovcloudapi.net", + TrafficManagerDNSSuffix: "usgovtrafficmanager.net", + KeyVaultDNSSuffix: "vault.usgovcloudapi.net", + ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net", + ServiceManagementVMDNSSuffix: "usgovcloudapp.net", + ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us", + ContainerRegistryDNSSuffix: "azurecr.io", + } + + // ChinaCloud is the cloud environment operated in China + ChinaCloud = Environment{ + Name: "AzureChinaCloud", + ManagementPortalURL: "https://manage.chinacloudapi.com/", + PublishSettingsURL: "https://manage.chinacloudapi.com/publishsettings/index", + ServiceManagementEndpoint: "https://management.core.chinacloudapi.cn/", + ResourceManagerEndpoint: "https://management.chinacloudapi.cn/", + ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/", + GalleryEndpoint: "https://gallery.chinacloudapi.cn/", + KeyVaultEndpoint: "https://vault.azure.cn/", + GraphEndpoint: "https://graph.chinacloudapi.cn/", + StorageEndpointSuffix: "core.chinacloudapi.cn", + SQLDatabaseDNSSuffix: "database.chinacloudapi.cn", + TrafficManagerDNSSuffix: "trafficmanager.cn", + KeyVaultDNSSuffix: "vault.azure.cn", + ServiceBusEndpointSuffix: "servicebus.chinacloudapi.net", + ServiceManagementVMDNSSuffix: "chinacloudapp.cn", + ResourceManagerVMDNSSuffix: "cloudapp.azure.cn", + ContainerRegistryDNSSuffix: "azurecr.io", + } + + // GermanCloud is the cloud environment operated in Germany + GermanCloud = Environment{ + Name: "AzureGermanCloud", + ManagementPortalURL: "http://portal.microsoftazure.de/", + PublishSettingsURL: "https://manage.microsoftazure.de/publishsettings/index", + ServiceManagementEndpoint: "https://management.core.cloudapi.de/", + ResourceManagerEndpoint: "https://management.microsoftazure.de/", + ActiveDirectoryEndpoint: "https://login.microsoftonline.de/", + GalleryEndpoint: "https://gallery.cloudapi.de/", + KeyVaultEndpoint: "https://vault.microsoftazure.de/", + GraphEndpoint: "https://graph.cloudapi.de/", + StorageEndpointSuffix: "core.cloudapi.de", + SQLDatabaseDNSSuffix: "database.cloudapi.de", + TrafficManagerDNSSuffix: "azuretrafficmanager.de", + KeyVaultDNSSuffix: "vault.microsoftazure.de", + ServiceBusEndpointSuffix: "servicebus.cloudapi.de", + ServiceManagementVMDNSSuffix: "azurecloudapp.de", + ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de", + ContainerRegistryDNSSuffix: "azurecr.io", + } +) + +// EnvironmentFromName returns an Environment based on the common name specified +func EnvironmentFromName(name string) (Environment, error) { + name = strings.ToUpper(name) + env, ok := environments[name] + if !ok { + return env, fmt.Errorf("autorest/azure: There is no cloud environment matching the name %q", name) + } + return env, nil +} + +// OAuthConfigForTenant returns an OAuthConfig with tenant specific urls +func (env Environment) OAuthConfigForTenant(tenantID string) (*OAuthConfig, error) { + return OAuthConfigForTenant(env.ActiveDirectoryEndpoint, tenantID) +} + +// OAuthConfigForTenant returns an OAuthConfig with tenant specific urls for target cloud auth endpoint +func OAuthConfigForTenant(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) { + template := "%s/oauth2/%s?api-version=%s" + u, err := url.Parse(activeDirectoryEndpoint) + if err != nil { + return nil, err + } + authorizeURL, err := u.Parse(fmt.Sprintf(template, tenantID, "authorize", activeDirectoryAPIVersion)) + if err != nil { + return nil, err + } + tokenURL, err := u.Parse(fmt.Sprintf(template, tenantID, "token", activeDirectoryAPIVersion)) + if err != nil { + return nil, err + } + deviceCodeURL, err := u.Parse(fmt.Sprintf(template, tenantID, "devicecode", activeDirectoryAPIVersion)) + if err != nil { + return nil, err + } + + return &OAuthConfig{ + AuthorizeEndpoint: *authorizeURL, + TokenEndpoint: *tokenURL, + DeviceCodeEndpoint: *deviceCodeURL, + }, nil +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/persist.go b/vendor/github.com/Azure/go-autorest/autorest/azure/persist.go new file mode 100644 index 000000000..d5cf62ddc --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/persist.go @@ -0,0 +1,59 @@ +package azure + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +// LoadToken restores a Token object from a file located at 'path'. +func LoadToken(path string) (*Token, error) { + file, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) + } + defer file.Close() + + var token Token + + dec := json.NewDecoder(file) + if err = dec.Decode(&token); err != nil { + return nil, fmt.Errorf("failed to decode contents of file (%s) into Token representation: %v", path, err) + } + return &token, nil +} + +// SaveToken persists an oauth token at the given location on disk. +// It moves the new file into place so it can safely be used to replace an existing file +// that maybe accessed by multiple processes. +func SaveToken(path string, mode os.FileMode, token Token) error { + dir := filepath.Dir(path) + err := os.MkdirAll(dir, os.ModePerm) + if err != nil { + return fmt.Errorf("failed to create directory (%s) to store token in: %v", dir, err) + } + + newFile, err := ioutil.TempFile(dir, "token") + if err != nil { + return fmt.Errorf("failed to create the temp file to write the token: %v", err) + } + tempPath := newFile.Name() + + if err := json.NewEncoder(newFile).Encode(token); err != nil { + return fmt.Errorf("failed to encode token to file (%s) while saving token: %v", tempPath, err) + } + if err := newFile.Close(); err != nil { + return fmt.Errorf("failed to close temp file %s: %v", tempPath, err) + } + + // Atomic replace to avoid multi-writer file corruptions + if err := os.Rename(tempPath, path); err != nil { + return fmt.Errorf("failed to move temporary token to desired output location. src=%s dst=%s: %v", tempPath, path, err) + } + if err := os.Chmod(path, mode); err != nil { + return fmt.Errorf("failed to chmod the token file %s: %v", path, err) + } + return nil +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/token.go b/vendor/github.com/Azure/go-autorest/autorest/azure/token.go new file mode 100644 index 000000000..cfcd03011 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/token.go @@ -0,0 +1,363 @@ +package azure + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "encoding/base64" + "fmt" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/Azure/go-autorest/autorest" + "github.com/dgrijalva/jwt-go" +) + +const ( + defaultRefresh = 5 * time.Minute + tokenBaseDate = "1970-01-01T00:00:00Z" + + // OAuthGrantTypeDeviceCode is the "grant_type" identifier used in device flow + OAuthGrantTypeDeviceCode = "device_code" + + // OAuthGrantTypeClientCredentials is the "grant_type" identifier used in credential flows + OAuthGrantTypeClientCredentials = "client_credentials" + + // OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows + OAuthGrantTypeRefreshToken = "refresh_token" +) + +var expirationBase time.Time + +func init() { + expirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate) +} + +// TokenRefreshCallback is the type representing callbacks that will be called after +// a successful token refresh +type TokenRefreshCallback func(Token) error + +// Token encapsulates the access token used to authorize Azure requests. +type Token struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + + ExpiresIn string `json:"expires_in"` + ExpiresOn string `json:"expires_on"` + NotBefore string `json:"not_before"` + + Resource string `json:"resource"` + Type string `json:"token_type"` +} + +// Expires returns the time.Time when the Token expires. +func (t Token) Expires() time.Time { + s, err := strconv.Atoi(t.ExpiresOn) + if err != nil { + s = -3600 + } + return expirationBase.Add(time.Duration(s) * time.Second).UTC() +} + +// IsExpired returns true if the Token is expired, false otherwise. +func (t Token) IsExpired() bool { + return t.WillExpireIn(0) +} + +// WillExpireIn returns true if the Token will expire after the passed time.Duration interval +// from now, false otherwise. +func (t Token) WillExpireIn(d time.Duration) bool { + return !t.Expires().After(time.Now().Add(d)) +} + +// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose +// value is "Bearer " followed by the AccessToken of the Token. +func (t *Token) WithAuthorization() autorest.PrepareDecorator { + return func(p autorest.Preparer) autorest.Preparer { + return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + return (autorest.WithBearerAuthorization(t.AccessToken)(p)).Prepare(r) + }) + } +} + +// ServicePrincipalNoSecret represents a secret type that contains no secret +// meaning it is not valid for fetching a fresh token. This is used by Manual +type ServicePrincipalNoSecret struct { +} + +// SetAuthenticationValues is a method of the interface ServicePrincipalSecret +// It only returns an error for the ServicePrincipalNoSecret type +func (noSecret *ServicePrincipalNoSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { + return fmt.Errorf("Manually created ServicePrincipalToken does not contain secret material to retrieve a new access token") +} + +// ServicePrincipalSecret is an interface that allows various secret mechanism to fill the form +// that is submitted when acquiring an oAuth token. +type ServicePrincipalSecret interface { + SetAuthenticationValues(spt *ServicePrincipalToken, values *url.Values) error +} + +// ServicePrincipalTokenSecret implements ServicePrincipalSecret for client_secret type authorization. +type ServicePrincipalTokenSecret struct { + ClientSecret string +} + +// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. +// It will populate the form submitted during oAuth Token Acquisition using the client_secret. +func (tokenSecret *ServicePrincipalTokenSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { + v.Set("client_secret", tokenSecret.ClientSecret) + return nil +} + +// ServicePrincipalCertificateSecret implements ServicePrincipalSecret for generic RSA cert auth with signed JWTs. +type ServicePrincipalCertificateSecret struct { + Certificate *x509.Certificate + PrivateKey *rsa.PrivateKey +} + +// SignJwt returns the JWT signed with the certificate's private key. +func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) { + hasher := sha1.New() + _, err := hasher.Write(secret.Certificate.Raw) + if err != nil { + return "", err + } + + thumbprint := base64.URLEncoding.EncodeToString(hasher.Sum(nil)) + + // The jti (JWT ID) claim provides a unique identifier for the JWT. + jti := make([]byte, 20) + _, err = rand.Read(jti) + if err != nil { + return "", err + } + + token := jwt.New(jwt.SigningMethodRS256) + token.Header["x5t"] = thumbprint + token.Claims = jwt.MapClaims{ + "aud": spt.oauthConfig.TokenEndpoint.String(), + "iss": spt.clientID, + "sub": spt.clientID, + "jti": base64.URLEncoding.EncodeToString(jti), + "nbf": time.Now().Unix(), + "exp": time.Now().Add(time.Hour * 24).Unix(), + } + + signedString, err := token.SignedString(secret.PrivateKey) + return signedString, err +} + +// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. +// It will populate the form submitted during oAuth Token Acquisition using a JWT signed with a certificate. +func (secret *ServicePrincipalCertificateSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { + jwt, err := secret.SignJwt(spt) + if err != nil { + return err + } + + v.Set("client_assertion", jwt) + v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") + return nil +} + +// ServicePrincipalToken encapsulates a Token created for a Service Principal. +type ServicePrincipalToken struct { + Token + + secret ServicePrincipalSecret + oauthConfig OAuthConfig + clientID string + resource string + autoRefresh bool + refreshWithin time.Duration + sender autorest.Sender + + refreshCallbacks []TokenRefreshCallback +} + +// NewServicePrincipalTokenWithSecret create a ServicePrincipalToken using the supplied ServicePrincipalSecret implementation. +func NewServicePrincipalTokenWithSecret(oauthConfig OAuthConfig, id string, resource string, secret ServicePrincipalSecret, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + spt := &ServicePrincipalToken{ + oauthConfig: oauthConfig, + secret: secret, + clientID: id, + resource: resource, + autoRefresh: true, + refreshWithin: defaultRefresh, + sender: &http.Client{}, + refreshCallbacks: callbacks, + } + return spt, nil +} + +// NewServicePrincipalTokenFromManualToken creates a ServicePrincipalToken using the supplied token +func NewServicePrincipalTokenFromManualToken(oauthConfig OAuthConfig, clientID string, resource string, token Token, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + spt, err := NewServicePrincipalTokenWithSecret( + oauthConfig, + clientID, + resource, + &ServicePrincipalNoSecret{}, + callbacks...) + if err != nil { + return nil, err + } + + spt.Token = token + + return spt, nil +} + +// NewServicePrincipalToken creates a ServicePrincipalToken from the supplied Service Principal +// credentials scoped to the named resource. +func NewServicePrincipalToken(oauthConfig OAuthConfig, clientID string, secret string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + return NewServicePrincipalTokenWithSecret( + oauthConfig, + clientID, + resource, + &ServicePrincipalTokenSecret{ + ClientSecret: secret, + }, + callbacks..., + ) +} + +// NewServicePrincipalTokenFromCertificate create a ServicePrincipalToken from the supplied pkcs12 bytes. +func NewServicePrincipalTokenFromCertificate(oauthConfig OAuthConfig, clientID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { + return NewServicePrincipalTokenWithSecret( + oauthConfig, + clientID, + resource, + &ServicePrincipalCertificateSecret{ + PrivateKey: privateKey, + Certificate: certificate, + }, + callbacks..., + ) +} + +// EnsureFresh will refresh the token if it will expire within the refresh window (as set by +// RefreshWithin). +func (spt *ServicePrincipalToken) EnsureFresh() error { + if spt.WillExpireIn(spt.refreshWithin) { + return spt.Refresh() + } + return nil +} + +// InvokeRefreshCallbacks calls any TokenRefreshCallbacks that were added to the SPT during initialization +func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error { + if spt.refreshCallbacks != nil { + for _, callback := range spt.refreshCallbacks { + err := callback(spt.Token) + if err != nil { + return autorest.NewErrorWithError(err, + "azure.ServicePrincipalToken", "InvokeRefreshCallbacks", nil, "A TokenRefreshCallback handler returned an error") + } + } + } + return nil +} + +// Refresh obtains a fresh token for the Service Principal. +func (spt *ServicePrincipalToken) Refresh() error { + return spt.refreshInternal(spt.resource) +} + +// RefreshExchange refreshes the token, but for a different resource. +func (spt *ServicePrincipalToken) RefreshExchange(resource string) error { + return spt.refreshInternal(resource) +} + +func (spt *ServicePrincipalToken) refreshInternal(resource string) error { + v := url.Values{} + v.Set("client_id", spt.clientID) + v.Set("resource", resource) + + if spt.RefreshToken != "" { + v.Set("grant_type", OAuthGrantTypeRefreshToken) + v.Set("refresh_token", spt.RefreshToken) + } else { + v.Set("grant_type", OAuthGrantTypeClientCredentials) + err := spt.secret.SetAuthenticationValues(spt, &v) + if err != nil { + return err + } + } + + req, _ := autorest.Prepare(&http.Request{}, + autorest.AsPost(), + autorest.AsFormURLEncoded(), + autorest.WithBaseURL(spt.oauthConfig.TokenEndpoint.String()), + autorest.WithFormData(v)) + + resp, err := autorest.SendWithSender(spt.sender, req) + if err != nil { + return autorest.NewErrorWithError(err, + "azure.ServicePrincipalToken", "Refresh", resp, "Failure sending request for Service Principal %s", + spt.clientID) + } + + var newToken Token + err = autorest.Respond(resp, + autorest.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&newToken), + autorest.ByClosing()) + if err != nil { + return autorest.NewErrorWithError(err, + "azure.ServicePrincipalToken", "Refresh", resp, "Failure handling response to Service Principal %s request", + spt.clientID) + } + + spt.Token = newToken + + err = spt.InvokeRefreshCallbacks(newToken) + if err != nil { + // its already wrapped inside InvokeRefreshCallbacks + return err + } + + return nil +} + +// SetAutoRefresh enables or disables automatic refreshing of stale tokens. +func (spt *ServicePrincipalToken) SetAutoRefresh(autoRefresh bool) { + spt.autoRefresh = autoRefresh +} + +// SetRefreshWithin sets the interval within which if the token will expire, EnsureFresh will +// refresh the token. +func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) { + spt.refreshWithin = d + return +} + +// SetSender sets the autorest.Sender used when obtaining the Service Principal token. An +// undecorated http.Client is used by default. +func (spt *ServicePrincipalToken) SetSender(s autorest.Sender) { + spt.sender = s +} + +// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose +// value is "Bearer " followed by the AccessToken of the ServicePrincipalToken. +// +// By default, the token will automatically refresh if nearly expired (as determined by the +// RefreshWithin interval). Use the AutoRefresh method to enable or disable automatically refreshing +// tokens. +func (spt *ServicePrincipalToken) WithAuthorization() autorest.PrepareDecorator { + return func(p autorest.Preparer) autorest.Preparer { + return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { + if spt.autoRefresh { + err := spt.EnsureFresh() + if err != nil { + return r, autorest.NewErrorWithError(err, + "azure.ServicePrincipalToken", "WithAuthorization", nil, "Failed to refresh Service Principal Token for request to %s", + r.URL) + } + } + return (autorest.WithBearerAuthorization(spt.AccessToken)(p)).Prepare(r) + }) + } +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/client.go b/vendor/github.com/Azure/go-autorest/autorest/client.go new file mode 100644 index 000000000..b5f94b5c3 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/client.go @@ -0,0 +1,235 @@ +package autorest + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "net/http/cookiejar" + "runtime" + "time" +) + +const ( + // DefaultPollingDelay is a reasonable delay between polling requests. + DefaultPollingDelay = 60 * time.Second + + // DefaultPollingDuration is a reasonable total polling duration. + DefaultPollingDuration = 15 * time.Minute + + // DefaultRetryAttempts is number of attempts for retry status codes (5xx). + DefaultRetryAttempts = 3 +) + +var ( + // defaultUserAgent builds a string containing the Go version, system archityecture and OS, + // and the go-autorest version. + defaultUserAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s", + runtime.Version(), + runtime.GOARCH, + runtime.GOOS, + Version(), + ) + + statusCodesForRetry = []int{ + http.StatusRequestTimeout, // 408 + http.StatusInternalServerError, // 500 + http.StatusBadGateway, // 502 + http.StatusServiceUnavailable, // 503 + http.StatusGatewayTimeout, // 504 + } +) + +const ( + requestFormat = `HTTP Request Begin =================================================== +%s +===================================================== HTTP Request End +` + responseFormat = `HTTP Response Begin =================================================== +%s +===================================================== HTTP Response End +` +) + +// Response serves as the base for all responses from generated clients. It provides access to the +// last http.Response. +type Response struct { + *http.Response `json:"-"` +} + +// LoggingInspector implements request and response inspectors that log the full request and +// response to a supplied log. +type LoggingInspector struct { + Logger *log.Logger +} + +// WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The +// body is restored after being emitted. +// +// Note: Since it reads the entire Body, this decorator should not be used where body streaming is +// important. It is best used to trace JSON or similar body values. +func (li LoggingInspector) WithInspection() PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + var body, b bytes.Buffer + + defer r.Body.Close() + + r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body)) + if err := r.Write(&b); err != nil { + return nil, fmt.Errorf("Failed to write response: %v", err) + } + + li.Logger.Printf(requestFormat, b.String()) + + r.Body = ioutil.NopCloser(&body) + return p.Prepare(r) + }) + } +} + +// ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The +// body is restored after being emitted. +// +// Note: Since it reads the entire Body, this decorator should not be used where body streaming is +// important. It is best used to trace JSON or similar body values. +func (li LoggingInspector) ByInspecting() RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + var body, b bytes.Buffer + defer resp.Body.Close() + resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body)) + if err := resp.Write(&b); err != nil { + return fmt.Errorf("Failed to write response: %v", err) + } + + li.Logger.Printf(responseFormat, b.String()) + + resp.Body = ioutil.NopCloser(&body) + return r.Respond(resp) + }) + } +} + +// Client is the base for autorest generated clients. It provides default, "do nothing" +// implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the +// standard, undecorated http.Client as a default Sender. +// +// Generated clients should also use Error (see NewError and NewErrorWithError) for errors and +// return responses that compose with Response. +// +// Most customization of generated clients is best achieved by supplying a custom Authorizer, custom +// RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit +// breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence +// sending the request by providing a decorated Sender. +type Client struct { + Authorizer Authorizer + Sender Sender + RequestInspector PrepareDecorator + ResponseInspector RespondDecorator + + // PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header + PollingDelay time.Duration + + // PollingDuration sets the maximum polling time after which an error is returned. + PollingDuration time.Duration + + // RetryAttempts sets the default number of retry attempts for client. + RetryAttempts int + + // RetryDuration sets the delay duration for retries. + RetryDuration time.Duration + + // UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent + // through the Do method. + UserAgent string + + Jar http.CookieJar +} + +// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed +// string. +func NewClientWithUserAgent(ua string) Client { + c := Client{ + PollingDelay: DefaultPollingDelay, + PollingDuration: DefaultPollingDuration, + RetryAttempts: DefaultRetryAttempts, + RetryDuration: 30 * time.Second, + UserAgent: defaultUserAgent, + } + c.AddToUserAgent(ua) + return c +} + +// AddToUserAgent adds an extension to the current user agent +func (c *Client) AddToUserAgent(extension string) error { + if extension != "" { + c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension) + return nil + } + return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent) +} + +// Do implements the Sender interface by invoking the active Sender after applying authorization. +// If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent +// is set, apply set the User-Agent header. +func (c Client) Do(r *http.Request) (*http.Response, error) { + if r.UserAgent() == "" { + r, _ = Prepare(r, + WithUserAgent(c.UserAgent)) + } + r, err := Prepare(r, + c.WithInspection(), + c.WithAuthorization()) + if err != nil { + return nil, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed") + } + resp, err := SendWithSender(c.sender(), r, + DoRetryForStatusCodes(c.RetryAttempts, c.RetryDuration, statusCodesForRetry...)) + Respond(resp, + c.ByInspecting()) + return resp, err +} + +// sender returns the Sender to which to send requests. +func (c Client) sender() Sender { + if c.Sender == nil { + j, _ := cookiejar.New(nil) + return &http.Client{Jar: j} + } + return c.Sender +} + +// WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator +// from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer. +func (c Client) WithAuthorization() PrepareDecorator { + return c.authorizer().WithAuthorization() +} + +// authorizer returns the Authorizer to use. +func (c Client) authorizer() Authorizer { + if c.Authorizer == nil { + return NullAuthorizer{} + } + return c.Authorizer +} + +// WithInspection is a convenience method that passes the request to the supplied RequestInspector, +// if present, or returns the WithNothing PrepareDecorator otherwise. +func (c Client) WithInspection() PrepareDecorator { + if c.RequestInspector == nil { + return WithNothing() + } + return c.RequestInspector +} + +// ByInspecting is a convenience method that passes the response to the supplied ResponseInspector, +// if present, or returns the ByIgnoring RespondDecorator otherwise. +func (c Client) ByInspecting() RespondDecorator { + if c.ResponseInspector == nil { + return ByIgnoring() + } + return c.ResponseInspector +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/date.go b/vendor/github.com/Azure/go-autorest/autorest/date/date.go new file mode 100644 index 000000000..80ca60e9b --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/date/date.go @@ -0,0 +1,82 @@ +/* +Package date provides time.Time derivatives that conform to the Swagger.io (https://swagger.io/) +defined date formats: Date and DateTime. Both types may, in most cases, be used in lieu of +time.Time types. And both convert to time.Time through a ToTime method. +*/ +package date + +import ( + "fmt" + "time" +) + +const ( + fullDate = "2006-01-02" + fullDateJSON = `"2006-01-02"` + dateFormat = "%04d-%02d-%02d" + jsonFormat = `"%04d-%02d-%02d"` +) + +// Date defines a type similar to time.Time but assumes a layout of RFC3339 full-date (i.e., +// 2006-01-02). +type Date struct { + time.Time +} + +// ParseDate create a new Date from the passed string. +func ParseDate(date string) (d Date, err error) { + return parseDate(date, fullDate) +} + +func parseDate(date string, format string) (Date, error) { + d, err := time.Parse(format, date) + return Date{Time: d}, err +} + +// MarshalBinary preserves the Date as a byte array conforming to RFC3339 full-date (i.e., +// 2006-01-02). +func (d Date) MarshalBinary() ([]byte, error) { + return d.MarshalText() +} + +// UnmarshalBinary reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e., +// 2006-01-02). +func (d *Date) UnmarshalBinary(data []byte) error { + return d.UnmarshalText(data) +} + +// MarshalJSON preserves the Date as a JSON string conforming to RFC3339 full-date (i.e., +// 2006-01-02). +func (d Date) MarshalJSON() (json []byte, err error) { + return []byte(fmt.Sprintf(jsonFormat, d.Year(), d.Month(), d.Day())), nil +} + +// UnmarshalJSON reconstitutes the Date from a JSON string conforming to RFC3339 full-date (i.e., +// 2006-01-02). +func (d *Date) UnmarshalJSON(data []byte) (err error) { + d.Time, err = time.Parse(fullDateJSON, string(data)) + return err +} + +// MarshalText preserves the Date as a byte array conforming to RFC3339 full-date (i.e., +// 2006-01-02). +func (d Date) MarshalText() (text []byte, err error) { + return []byte(fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day())), nil +} + +// UnmarshalText reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e., +// 2006-01-02). +func (d *Date) UnmarshalText(data []byte) (err error) { + d.Time, err = time.Parse(fullDate, string(data)) + return err +} + +// String returns the Date formatted as an RFC3339 full-date string (i.e., 2006-01-02). +func (d Date) String() string { + return fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day()) +} + +// ToTime returns a Date as a time.Time +func (d Date) ToTime() time.Time { + return d.Time +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/time.go b/vendor/github.com/Azure/go-autorest/autorest/date/time.go new file mode 100644 index 000000000..c1af62963 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/date/time.go @@ -0,0 +1,89 @@ +package date + +import ( + "regexp" + "time" +) + +// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases. +const ( + azureUtcFormatJSON = `"2006-01-02T15:04:05.999999999"` + azureUtcFormat = "2006-01-02T15:04:05.999999999" + rfc3339JSON = `"` + time.RFC3339Nano + `"` + rfc3339 = time.RFC3339Nano + tzOffsetRegex = `(Z|z|\+|-)(\d+:\d+)*"*$` +) + +// Time defines a type similar to time.Time but assumes a layout of RFC3339 date-time (i.e., +// 2006-01-02T15:04:05Z). +type Time struct { + time.Time +} + +// MarshalBinary preserves the Time as a byte array conforming to RFC3339 date-time (i.e., +// 2006-01-02T15:04:05Z). +func (t Time) MarshalBinary() ([]byte, error) { + return t.Time.MarshalText() +} + +// UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC3339 date-time +// (i.e., 2006-01-02T15:04:05Z). +func (t *Time) UnmarshalBinary(data []byte) error { + return t.UnmarshalText(data) +} + +// MarshalJSON preserves the Time as a JSON string conforming to RFC3339 date-time (i.e., +// 2006-01-02T15:04:05Z). +func (t Time) MarshalJSON() (json []byte, err error) { + return t.Time.MarshalJSON() +} + +// UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC3339 date-time +// (i.e., 2006-01-02T15:04:05Z). +func (t *Time) UnmarshalJSON(data []byte) (err error) { + timeFormat := azureUtcFormatJSON + match, err := regexp.Match(tzOffsetRegex, data) + if err != nil { + return err + } else if match { + timeFormat = rfc3339JSON + } + t.Time, err = ParseTime(timeFormat, string(data)) + return err +} + +// MarshalText preserves the Time as a byte array conforming to RFC3339 date-time (i.e., +// 2006-01-02T15:04:05Z). +func (t Time) MarshalText() (text []byte, err error) { + return t.Time.MarshalText() +} + +// UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC3339 date-time +// (i.e., 2006-01-02T15:04:05Z). +func (t *Time) UnmarshalText(data []byte) (err error) { + timeFormat := azureUtcFormat + match, err := regexp.Match(tzOffsetRegex, data) + if err != nil { + return err + } else if match { + timeFormat = rfc3339 + } + t.Time, err = ParseTime(timeFormat, string(data)) + return err +} + +// String returns the Time formatted as an RFC3339 date-time string (i.e., +// 2006-01-02T15:04:05Z). +func (t Time) String() string { + // Note: time.Time.String does not return an RFC3339 compliant string, time.Time.MarshalText does. + b, err := t.MarshalText() + if err != nil { + return "" + } + return string(b) +} + +// ToTime returns a Time as a time.Time +func (t Time) ToTime() time.Time { + return t.Time +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go b/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go new file mode 100644 index 000000000..11995fb9f --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go @@ -0,0 +1,86 @@ +package date + +import ( + "errors" + "time" +) + +const ( + rfc1123JSON = `"` + time.RFC1123 + `"` + rfc1123 = time.RFC1123 +) + +// TimeRFC1123 defines a type similar to time.Time but assumes a layout of RFC1123 date-time (i.e., +// Mon, 02 Jan 2006 15:04:05 MST). +type TimeRFC1123 struct { + time.Time +} + +// UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC1123 date-time +// (i.e., Mon, 02 Jan 2006 15:04:05 MST). +func (t *TimeRFC1123) UnmarshalJSON(data []byte) (err error) { + t.Time, err = ParseTime(rfc1123JSON, string(data)) + if err != nil { + return err + } + return nil +} + +// MarshalJSON preserves the Time as a JSON string conforming to RFC1123 date-time (i.e., +// Mon, 02 Jan 2006 15:04:05 MST). +func (t TimeRFC1123) MarshalJSON() ([]byte, error) { + if y := t.Year(); y < 0 || y >= 10000 { + return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]") + } + b := []byte(t.Format(rfc1123JSON)) + return b, nil +} + +// MarshalText preserves the Time as a byte array conforming to RFC1123 date-time (i.e., +// Mon, 02 Jan 2006 15:04:05 MST). +func (t TimeRFC1123) MarshalText() ([]byte, error) { + if y := t.Year(); y < 0 || y >= 10000 { + return nil, errors.New("Time.MarshalText: year outside of range [0,9999]") + } + + b := []byte(t.Format(rfc1123)) + return b, nil +} + +// UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC1123 date-time +// (i.e., Mon, 02 Jan 2006 15:04:05 MST). +func (t *TimeRFC1123) UnmarshalText(data []byte) (err error) { + t.Time, err = ParseTime(rfc1123, string(data)) + if err != nil { + return err + } + return nil +} + +// MarshalBinary preserves the Time as a byte array conforming to RFC1123 date-time (i.e., +// Mon, 02 Jan 2006 15:04:05 MST). +func (t TimeRFC1123) MarshalBinary() ([]byte, error) { + return t.MarshalText() +} + +// UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC1123 date-time +// (i.e., Mon, 02 Jan 2006 15:04:05 MST). +func (t *TimeRFC1123) UnmarshalBinary(data []byte) error { + return t.UnmarshalText(data) +} + +// ToTime returns a Time as a time.Time +func (t TimeRFC1123) ToTime() time.Time { + return t.Time +} + +// String returns the Time formatted as an RFC1123 date-time string (i.e., +// Mon, 02 Jan 2006 15:04:05 MST). +func (t TimeRFC1123) String() string { + // Note: time.Time.String does not return an RFC1123 compliant string, time.Time.MarshalText does. + b, err := t.MarshalText() + if err != nil { + return "" + } + return string(b) +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/utility.go b/vendor/github.com/Azure/go-autorest/autorest/date/utility.go new file mode 100644 index 000000000..207b1a240 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/date/utility.go @@ -0,0 +1,11 @@ +package date + +import ( + "strings" + "time" +) + +// ParseTime to parse Time string to specified format. +func ParseTime(format string, t string) (d time.Time, err error) { + return time.Parse(format, strings.ToUpper(t)) +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/error.go b/vendor/github.com/Azure/go-autorest/autorest/error.go new file mode 100644 index 000000000..4bcb8f27b --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/error.go @@ -0,0 +1,80 @@ +package autorest + +import ( + "fmt" + "net/http" +) + +const ( + // UndefinedStatusCode is used when HTTP status code is not available for an error. + UndefinedStatusCode = 0 +) + +// DetailedError encloses a error with details of the package, method, and associated HTTP +// status code (if any). +type DetailedError struct { + Original error + + // PackageType is the package type of the object emitting the error. For types, the value + // matches that produced the the '%T' format specifier of the fmt package. For other elements, + // such as functions, it is just the package name (e.g., "autorest"). + PackageType string + + // Method is the name of the method raising the error. + Method string + + // StatusCode is the HTTP Response StatusCode (if non-zero) that led to the error. + StatusCode interface{} + + // Message is the error message. + Message string + + // Service Error is the response body of failed API in bytes + ServiceError []byte +} + +// NewError creates a new Error conforming object from the passed packageType, method, and +// message. message is treated as a format string to which the optional args apply. +func NewError(packageType string, method string, message string, args ...interface{}) DetailedError { + return NewErrorWithError(nil, packageType, method, nil, message, args...) +} + +// NewErrorWithResponse creates a new Error conforming object from the passed +// packageType, method, statusCode of the given resp (UndefinedStatusCode if +// resp is nil), and message. message is treated as a format string to which the +// optional args apply. +func NewErrorWithResponse(packageType string, method string, resp *http.Response, message string, args ...interface{}) DetailedError { + return NewErrorWithError(nil, packageType, method, resp, message, args...) +} + +// NewErrorWithError creates a new Error conforming object from the +// passed packageType, method, statusCode of the given resp (UndefinedStatusCode +// if resp is nil), message, and original error. message is treated as a format +// string to which the optional args apply. +func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) DetailedError { + if v, ok := original.(DetailedError); ok { + return v + } + + statusCode := UndefinedStatusCode + if resp != nil { + statusCode = resp.StatusCode + } + + return DetailedError{ + Original: original, + PackageType: packageType, + Method: method, + StatusCode: statusCode, + Message: fmt.Sprintf(message, args...), + } +} + +// Error returns a formatted containing all available details (i.e., PackageType, Method, +// StatusCode, Message, and original error (if any)). +func (e DetailedError) Error() string { + if e.Original == nil { + return fmt.Sprintf("%s#%s: %s: StatusCode=%d", e.PackageType, e.Method, e.Message, e.StatusCode) + } + return fmt.Sprintf("%s#%s: %s: StatusCode=%d -- Original Error: %v", e.PackageType, e.Method, e.Message, e.StatusCode, e.Original) +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/preparer.go b/vendor/github.com/Azure/go-autorest/autorest/preparer.go new file mode 100644 index 000000000..c9deb261a --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/preparer.go @@ -0,0 +1,443 @@ +package autorest + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "strings" +) + +const ( + mimeTypeJSON = "application/json" + mimeTypeFormPost = "application/x-www-form-urlencoded" + + headerAuthorization = "Authorization" + headerContentType = "Content-Type" + headerUserAgent = "User-Agent" +) + +// Preparer is the interface that wraps the Prepare method. +// +// Prepare accepts and possibly modifies an http.Request (e.g., adding Headers). Implementations +// must ensure to not share or hold per-invocation state since Preparers may be shared and re-used. +type Preparer interface { + Prepare(*http.Request) (*http.Request, error) +} + +// PreparerFunc is a method that implements the Preparer interface. +type PreparerFunc func(*http.Request) (*http.Request, error) + +// Prepare implements the Preparer interface on PreparerFunc. +func (pf PreparerFunc) Prepare(r *http.Request) (*http.Request, error) { + return pf(r) +} + +// PrepareDecorator takes and possibly decorates, by wrapping, a Preparer. Decorators may affect the +// http.Request and pass it along or, first, pass the http.Request along then affect the result. +type PrepareDecorator func(Preparer) Preparer + +// CreatePreparer creates, decorates, and returns a Preparer. +// Without decorators, the returned Preparer returns the passed http.Request unmodified. +// Preparers are safe to share and re-use. +func CreatePreparer(decorators ...PrepareDecorator) Preparer { + return DecoratePreparer( + Preparer(PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, nil })), + decorators...) +} + +// DecoratePreparer accepts a Preparer and a, possibly empty, set of PrepareDecorators, which it +// applies to the Preparer. Decorators are applied in the order received, but their affect upon the +// request depends on whether they are a pre-decorator (change the http.Request and then pass it +// along) or a post-decorator (pass the http.Request along and alter it on return). +func DecoratePreparer(p Preparer, decorators ...PrepareDecorator) Preparer { + for _, decorate := range decorators { + p = decorate(p) + } + return p +} + +// Prepare accepts an http.Request and a, possibly empty, set of PrepareDecorators. +// It creates a Preparer from the decorators which it then applies to the passed http.Request. +func Prepare(r *http.Request, decorators ...PrepareDecorator) (*http.Request, error) { + if r == nil { + return nil, NewError("autorest", "Prepare", "Invoked without an http.Request") + } + return CreatePreparer(decorators...).Prepare(r) +} + +// WithNothing returns a "do nothing" PrepareDecorator that makes no changes to the passed +// http.Request. +func WithNothing() PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + return p.Prepare(r) + }) + } +} + +// WithHeader returns a PrepareDecorator that sets the specified HTTP header of the http.Request to +// the passed value. It canonicalizes the passed header name (via http.CanonicalHeaderKey) before +// adding the header. +func WithHeader(header string, value string) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + if r.Header == nil { + r.Header = make(http.Header) + } + r.Header.Set(http.CanonicalHeaderKey(header), value) + } + return r, err + }) + } +} + +// WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose +// value is "Bearer " followed by the supplied token. +func WithBearerAuthorization(token string) PrepareDecorator { + return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", token)) +} + +// AsContentType returns a PrepareDecorator that adds an HTTP Content-Type header whose value +// is the passed contentType. +func AsContentType(contentType string) PrepareDecorator { + return WithHeader(headerContentType, contentType) +} + +// WithUserAgent returns a PrepareDecorator that adds an HTTP User-Agent header whose value is the +// passed string. +func WithUserAgent(ua string) PrepareDecorator { + return WithHeader(headerUserAgent, ua) +} + +// AsFormURLEncoded returns a PrepareDecorator that adds an HTTP Content-Type header whose value is +// "application/x-www-form-urlencoded". +func AsFormURLEncoded() PrepareDecorator { + return AsContentType(mimeTypeFormPost) +} + +// AsJSON returns a PrepareDecorator that adds an HTTP Content-Type header whose value is +// "application/json". +func AsJSON() PrepareDecorator { + return AsContentType(mimeTypeJSON) +} + +// WithMethod returns a PrepareDecorator that sets the HTTP method of the passed request. The +// decorator does not validate that the passed method string is a known HTTP method. +func WithMethod(method string) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r.Method = method + return p.Prepare(r) + }) + } +} + +// AsDelete returns a PrepareDecorator that sets the HTTP method to DELETE. +func AsDelete() PrepareDecorator { return WithMethod("DELETE") } + +// AsGet returns a PrepareDecorator that sets the HTTP method to GET. +func AsGet() PrepareDecorator { return WithMethod("GET") } + +// AsHead returns a PrepareDecorator that sets the HTTP method to HEAD. +func AsHead() PrepareDecorator { return WithMethod("HEAD") } + +// AsOptions returns a PrepareDecorator that sets the HTTP method to OPTIONS. +func AsOptions() PrepareDecorator { return WithMethod("OPTIONS") } + +// AsPatch returns a PrepareDecorator that sets the HTTP method to PATCH. +func AsPatch() PrepareDecorator { return WithMethod("PATCH") } + +// AsPost returns a PrepareDecorator that sets the HTTP method to POST. +func AsPost() PrepareDecorator { return WithMethod("POST") } + +// AsPut returns a PrepareDecorator that sets the HTTP method to PUT. +func AsPut() PrepareDecorator { return WithMethod("PUT") } + +// WithBaseURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed +// from the supplied baseUrl. +func WithBaseURL(baseURL string) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + var u *url.URL + if u, err = url.Parse(baseURL); err != nil { + return r, err + } + if u.Scheme == "" { + err = fmt.Errorf("autorest: No scheme detected in URL %s", baseURL) + } + if err == nil { + r.URL = u + } + } + return r, err + }) + } +} + +// WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the +// request base URL (i.e., http.Request.URL) with the corresponding values from the passed map. +func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator { + parameters := ensureValueStrings(urlParameters) + for key, value := range parameters { + baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1) + } + return WithBaseURL(baseURL) +} + +// WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the +// http.Request body. +func WithFormData(v url.Values) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + s := v.Encode() + r.ContentLength = int64(len(s)) + r.Body = ioutil.NopCloser(strings.NewReader(s)) + } + return r, err + }) + } +} + +// WithMultiPartFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) form parameters +// into the http.Request body. +func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + var body bytes.Buffer + writer := multipart.NewWriter(&body) + for key, value := range formDataParameters { + if rc, ok := value.(io.ReadCloser); ok { + var fd io.Writer + if fd, err = writer.CreateFormFile(key, key); err != nil { + return r, err + } + if _, err = io.Copy(fd, rc); err != nil { + return r, err + } + } else { + if err = writer.WriteField(key, ensureValueString(value)); err != nil { + return r, err + } + } + } + if err = writer.Close(); err != nil { + return r, err + } + if r.Header == nil { + r.Header = make(http.Header) + } + r.Header.Set(http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType()) + r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) + r.ContentLength = int64(body.Len()) + return r, err + } + return r, err + }) + } +} + +// WithFile returns a PrepareDecorator that sends file in request body. +func WithFile(f io.ReadCloser) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + b, err := ioutil.ReadAll(f) + if err != nil { + return r, err + } + r.Body = ioutil.NopCloser(bytes.NewReader(b)) + r.ContentLength = int64(len(b)) + } + return r, err + }) + } +} + +// WithBool returns a PrepareDecorator that encodes the passed bool into the body of the request +// and sets the Content-Length header. +func WithBool(v bool) PrepareDecorator { + return WithString(fmt.Sprintf("%v", v)) +} + +// WithFloat32 returns a PrepareDecorator that encodes the passed float32 into the body of the +// request and sets the Content-Length header. +func WithFloat32(v float32) PrepareDecorator { + return WithString(fmt.Sprintf("%v", v)) +} + +// WithFloat64 returns a PrepareDecorator that encodes the passed float64 into the body of the +// request and sets the Content-Length header. +func WithFloat64(v float64) PrepareDecorator { + return WithString(fmt.Sprintf("%v", v)) +} + +// WithInt32 returns a PrepareDecorator that encodes the passed int32 into the body of the request +// and sets the Content-Length header. +func WithInt32(v int32) PrepareDecorator { + return WithString(fmt.Sprintf("%v", v)) +} + +// WithInt64 returns a PrepareDecorator that encodes the passed int64 into the body of the request +// and sets the Content-Length header. +func WithInt64(v int64) PrepareDecorator { + return WithString(fmt.Sprintf("%v", v)) +} + +// WithString returns a PrepareDecorator that encodes the passed string into the body of the request +// and sets the Content-Length header. +func WithString(v string) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + r.ContentLength = int64(len(v)) + r.Body = ioutil.NopCloser(strings.NewReader(v)) + } + return r, err + }) + } +} + +// WithJSON returns a PrepareDecorator that encodes the data passed as JSON into the body of the +// request and sets the Content-Length header. +func WithJSON(v interface{}) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + b, err := json.Marshal(v) + if err == nil { + r.ContentLength = int64(len(b)) + r.Body = ioutil.NopCloser(bytes.NewReader(b)) + } + } + return r, err + }) + } +} + +// WithPath returns a PrepareDecorator that adds the supplied path to the request URL. If the path +// is absolute (that is, it begins with a "/"), it replaces the existing path. +func WithPath(path string) PrepareDecorator { + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + if r.URL == nil { + return r, NewError("autorest", "WithPath", "Invoked with a nil URL") + } + if r.URL, err = parseURL(r.URL, path); err != nil { + return r, err + } + } + return r, err + }) + } +} + +// WithEscapedPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the +// request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. The +// values will be escaped (aka URL encoded) before insertion into the path. +func WithEscapedPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator { + parameters := escapeValueStrings(ensureValueStrings(pathParameters)) + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + if r.URL == nil { + return r, NewError("autorest", "WithEscapedPathParameters", "Invoked with a nil URL") + } + for key, value := range parameters { + path = strings.Replace(path, "{"+key+"}", value, -1) + } + if r.URL, err = parseURL(r.URL, path); err != nil { + return r, err + } + } + return r, err + }) + } +} + +// WithPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the +// request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. +func WithPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator { + parameters := ensureValueStrings(pathParameters) + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + if r.URL == nil { + return r, NewError("autorest", "WithPathParameters", "Invoked with a nil URL") + } + for key, value := range parameters { + path = strings.Replace(path, "{"+key+"}", value, -1) + } + + if r.URL, err = parseURL(r.URL, path); err != nil { + return r, err + } + } + return r, err + }) + } +} + +func parseURL(u *url.URL, path string) (*url.URL, error) { + p := strings.TrimRight(u.String(), "/") + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + return url.Parse(p + path) +} + +// WithQueryParameters returns a PrepareDecorators that encodes and applies the query parameters +// given in the supplied map (i.e., key=value). +func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorator { + parameters := ensureValueStrings(queryParameters) + return func(p Preparer) Preparer { + return PreparerFunc(func(r *http.Request) (*http.Request, error) { + r, err := p.Prepare(r) + if err == nil { + if r.URL == nil { + return r, NewError("autorest", "WithQueryParameters", "Invoked with a nil URL") + } + v := r.URL.Query() + for key, value := range parameters { + v.Add(key, value) + } + r.URL.RawQuery = createQuery(v) + } + return r, err + }) + } +} + +// Authorizer is the interface that provides a PrepareDecorator used to supply request +// authorization. Most often, the Authorizer decorator runs last so it has access to the full +// state of the formed HTTP request. +type Authorizer interface { + WithAuthorization() PrepareDecorator +} + +// NullAuthorizer implements a default, "do nothing" Authorizer. +type NullAuthorizer struct{} + +// WithAuthorization returns a PrepareDecorator that does nothing. +func (na NullAuthorizer) WithAuthorization() PrepareDecorator { + return WithNothing() +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/responder.go b/vendor/github.com/Azure/go-autorest/autorest/responder.go new file mode 100644 index 000000000..87f71e585 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/responder.go @@ -0,0 +1,236 @@ +package autorest + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" +) + +// Responder is the interface that wraps the Respond method. +// +// Respond accepts and reacts to an http.Response. Implementations must ensure to not share or hold +// state since Responders may be shared and re-used. +type Responder interface { + Respond(*http.Response) error +} + +// ResponderFunc is a method that implements the Responder interface. +type ResponderFunc func(*http.Response) error + +// Respond implements the Responder interface on ResponderFunc. +func (rf ResponderFunc) Respond(r *http.Response) error { + return rf(r) +} + +// RespondDecorator takes and possibly decorates, by wrapping, a Responder. Decorators may react to +// the http.Response and pass it along or, first, pass the http.Response along then react. +type RespondDecorator func(Responder) Responder + +// CreateResponder creates, decorates, and returns a Responder. Without decorators, the returned +// Responder returns the passed http.Response unmodified. Responders may or may not be safe to share +// and re-used: It depends on the applied decorators. For example, a standard decorator that closes +// the response body is fine to share whereas a decorator that reads the body into a passed struct +// is not. +// +// To prevent memory leaks, ensure that at least one Responder closes the response body. +func CreateResponder(decorators ...RespondDecorator) Responder { + return DecorateResponder( + Responder(ResponderFunc(func(r *http.Response) error { return nil })), + decorators...) +} + +// DecorateResponder accepts a Responder and a, possibly empty, set of RespondDecorators, which it +// applies to the Responder. Decorators are applied in the order received, but their affect upon the +// request depends on whether they are a pre-decorator (react to the http.Response and then pass it +// along) or a post-decorator (pass the http.Response along and then react). +func DecorateResponder(r Responder, decorators ...RespondDecorator) Responder { + for _, decorate := range decorators { + r = decorate(r) + } + return r +} + +// Respond accepts an http.Response and a, possibly empty, set of RespondDecorators. +// It creates a Responder from the decorators it then applies to the passed http.Response. +func Respond(r *http.Response, decorators ...RespondDecorator) error { + if r == nil { + return nil + } + return CreateResponder(decorators...).Respond(r) +} + +// ByIgnoring returns a RespondDecorator that ignores the passed http.Response passing it unexamined +// to the next RespondDecorator. +func ByIgnoring() RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + return r.Respond(resp) + }) + } +} + +// ByCopying copies the contents of the http.Response Body into the passed bytes.Buffer as +// the Body is read. +func ByCopying(b *bytes.Buffer) RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + err := r.Respond(resp) + if err == nil && resp != nil && resp.Body != nil { + resp.Body = TeeReadCloser(resp.Body, b) + } + return err + }) + } +} + +// ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which +// it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed +// Responder is invoked prior to discarding the response body, the decorator may occur anywhere +// within the set. +func ByDiscardingBody() RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + err := r.Respond(resp) + if err == nil && resp != nil && resp.Body != nil { + if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { + return fmt.Errorf("Error discarding the response body: %v", err) + } + } + return err + }) + } +} + +// ByClosing returns a RespondDecorator that first invokes the passed Responder after which it +// closes the response body. Since the passed Responder is invoked prior to closing the response +// body, the decorator may occur anywhere within the set. +func ByClosing() RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + err := r.Respond(resp) + if resp != nil && resp.Body != nil { + if err := resp.Body.Close(); err != nil { + return fmt.Errorf("Error closing the response body: %v", err) + } + } + return err + }) + } +} + +// ByClosingIfError returns a RespondDecorator that first invokes the passed Responder after which +// it closes the response if the passed Responder returns an error and the response body exists. +func ByClosingIfError() RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + err := r.Respond(resp) + if err != nil && resp != nil && resp.Body != nil { + if err := resp.Body.Close(); err != nil { + return fmt.Errorf("Error closing the response body: %v", err) + } + } + return err + }) + } +} + +// ByUnmarshallingJSON returns a RespondDecorator that decodes a JSON document returned in the +// response Body into the value pointed to by v. +func ByUnmarshallingJSON(v interface{}) RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + err := r.Respond(resp) + if err == nil { + b, errInner := ioutil.ReadAll(resp.Body) + // Some responses might include a BOM, remove for successful unmarshalling + b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf")) + if errInner != nil { + err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) + } else if len(strings.Trim(string(b), " ")) > 0 { + errInner = json.Unmarshal(b, v) + if errInner != nil { + err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b)) + } + } + } + return err + }) + } +} + +// ByUnmarshallingXML returns a RespondDecorator that decodes a XML document returned in the +// response Body into the value pointed to by v. +func ByUnmarshallingXML(v interface{}) RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + err := r.Respond(resp) + if err == nil { + b, errInner := ioutil.ReadAll(resp.Body) + if errInner != nil { + err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) + } else { + errInner = xml.Unmarshal(b, v) + if errInner != nil { + err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b)) + } + } + } + return err + }) + } +} + +// WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response +// StatusCode is among the set passed. On error, response body is fully read into a buffer and +// presented in the returned error, as well as in the response body. +func WithErrorUnlessStatusCode(codes ...int) RespondDecorator { + return func(r Responder) Responder { + return ResponderFunc(func(resp *http.Response) error { + err := r.Respond(resp) + if err == nil && !ResponseHasStatusCode(resp, codes...) { + derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s", + resp.Request.Method, + resp.Request.URL, + resp.Status) + if resp.Body != nil { + defer resp.Body.Close() + b, _ := ioutil.ReadAll(resp.Body) + derr.ServiceError = b + resp.Body = ioutil.NopCloser(bytes.NewReader(b)) + } + err = derr + } + return err + }) + } +} + +// WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is +// anything other than HTTP 200. +func WithErrorUnlessOK() RespondDecorator { + return WithErrorUnlessStatusCode(http.StatusOK) +} + +// ExtractHeader extracts all values of the specified header from the http.Response. It returns an +// empty string slice if the passed http.Response is nil or the header does not exist. +func ExtractHeader(header string, resp *http.Response) []string { + if resp != nil && resp.Header != nil { + return resp.Header[http.CanonicalHeaderKey(header)] + } + return nil +} + +// ExtractHeaderValue extracts the first value of the specified header from the http.Response. It +// returns an empty string if the passed http.Response is nil or the header does not exist. +func ExtractHeaderValue(header string, resp *http.Response) string { + h := ExtractHeader(header, resp) + if len(h) > 0 { + return h[0] + } + return "" +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/sender.go b/vendor/github.com/Azure/go-autorest/autorest/sender.go new file mode 100644 index 000000000..9c0697815 --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/sender.go @@ -0,0 +1,270 @@ +package autorest + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "math" + "net/http" + "time" +) + +// Sender is the interface that wraps the Do method to send HTTP requests. +// +// The standard http.Client conforms to this interface. +type Sender interface { + Do(*http.Request) (*http.Response, error) +} + +// SenderFunc is a method that implements the Sender interface. +type SenderFunc func(*http.Request) (*http.Response, error) + +// Do implements the Sender interface on SenderFunc. +func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) { + return sf(r) +} + +// SendDecorator takes and possibily decorates, by wrapping, a Sender. Decorators may affect the +// http.Request and pass it along or, first, pass the http.Request along then react to the +// http.Response result. +type SendDecorator func(Sender) Sender + +// CreateSender creates, decorates, and returns, as a Sender, the default http.Client. +func CreateSender(decorators ...SendDecorator) Sender { + return DecorateSender(&http.Client{}, decorators...) +} + +// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to +// the Sender. Decorators are applied in the order received, but their affect upon the request +// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a +// post-decorator (pass the http.Request along and react to the results in http.Response). +func DecorateSender(s Sender, decorators ...SendDecorator) Sender { + for _, decorate := range decorators { + s = decorate(s) + } + return s +} + +// Send sends, by means of the default http.Client, the passed http.Request, returning the +// http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which +// it will apply the http.Client before invoking the Do method. +// +// Send is a convenience method and not recommended for production. Advanced users should use +// SendWithSender, passing and sharing their own Sender (e.g., instance of http.Client). +// +// Send will not poll or retry requests. +func Send(r *http.Request, decorators ...SendDecorator) (*http.Response, error) { + return SendWithSender(&http.Client{}, r, decorators...) +} + +// SendWithSender sends the passed http.Request, through the provided Sender, returning the +// http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which +// it will apply the http.Client before invoking the Do method. +// +// SendWithSender will not poll or retry requests. +func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*http.Response, error) { + return DecorateSender(s, decorators...).Do(r) +} + +// AfterDelay returns a SendDecorator that delays for the passed time.Duration before +// invoking the Sender. The delay may be terminated by closing the optional channel on the +// http.Request. If canceled, no further Senders are invoked. +func AfterDelay(d time.Duration) SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (*http.Response, error) { + if !DelayForBackoff(d, 0, r.Cancel) { + return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay") + } + return s.Do(r) + }) + } +} + +// AsIs returns a SendDecorator that invokes the passed Sender without modifying the http.Request. +func AsIs() SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (*http.Response, error) { + return s.Do(r) + }) + } +} + +// DoCloseIfError returns a SendDecorator that first invokes the passed Sender after which +// it closes the response if the passed Sender returns an error and the response body exists. +func DoCloseIfError() SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (*http.Response, error) { + resp, err := s.Do(r) + if err != nil { + Respond(resp, ByDiscardingBody(), ByClosing()) + } + return resp, err + }) + } +} + +// DoErrorIfStatusCode returns a SendDecorator that emits an error if the response StatusCode is +// among the set passed. Since these are artificial errors, the response body may still require +// closing. +func DoErrorIfStatusCode(codes ...int) SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (*http.Response, error) { + resp, err := s.Do(r) + if err == nil && ResponseHasStatusCode(resp, codes...) { + err = NewErrorWithResponse("autorest", "DoErrorIfStatusCode", resp, "%v %v failed with %s", + resp.Request.Method, + resp.Request.URL, + resp.Status) + } + return resp, err + }) + } +} + +// DoErrorUnlessStatusCode returns a SendDecorator that emits an error unless the response +// StatusCode is among the set passed. Since these are artificial errors, the response body +// may still require closing. +func DoErrorUnlessStatusCode(codes ...int) SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (*http.Response, error) { + resp, err := s.Do(r) + if err == nil && !ResponseHasStatusCode(resp, codes...) { + err = NewErrorWithResponse("autorest", "DoErrorUnlessStatusCode", resp, "%v %v failed with %s", + resp.Request.Method, + resp.Request.URL, + resp.Status) + } + return resp, err + }) + } +} + +// DoPollForStatusCodes returns a SendDecorator that polls if the http.Response contains one of the +// passed status codes. It expects the http.Response to contain a Location header providing the +// URL at which to poll (using GET) and will poll until the time passed is equal to or greater than +// the supplied duration. It will delay between requests for the duration specified in the +// RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by +// closing the optional channel on the http.Request. +func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...int) SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { + resp, err = s.Do(r) + + if err == nil && ResponseHasStatusCode(resp, codes...) { + r, err = NewPollingRequest(resp, r.Cancel) + + for err == nil && ResponseHasStatusCode(resp, codes...) { + Respond(resp, + ByDiscardingBody(), + ByClosing()) + resp, err = SendWithSender(s, r, + AfterDelay(GetRetryAfter(resp, delay))) + } + } + + return resp, err + }) + } +} + +// DoRetryForAttempts returns a SendDecorator that retries a failed request for up to the specified +// number of attempts, exponentially backing off between requests using the supplied backoff +// time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on +// the http.Request. +func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { + for attempt := 0; attempt < attempts; attempt++ { + resp, err = s.Do(r) + if err == nil { + return resp, err + } + DelayForBackoff(backoff, attempt, r.Cancel) + } + return resp, err + }) + } +} + +// DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified +// number of attempts, exponentially backing off between requests using the supplied backoff +// time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on +// the http.Request. +func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { + b := []byte{} + if r.Body != nil { + b, err = ioutil.ReadAll(r.Body) + if err != nil { + return resp, err + } + } + + // Increment to add the first call (attempts denotes number of retries) + attempts++ + for attempt := 0; attempt < attempts; attempt++ { + r.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + resp, err = s.Do(r) + if err != nil || !ResponseHasStatusCode(resp, codes...) { + return resp, err + } + DelayForBackoff(backoff, attempt, r.Cancel) + } + return resp, err + }) + } +} + +// DoRetryForDuration returns a SendDecorator that retries the request until the total time is equal +// to or greater than the specified duration, exponentially backing off between requests using the +// supplied backoff time.Duration (which may be zero). Retrying may be canceled by closing the +// optional channel on the http.Request. +func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { + end := time.Now().Add(d) + for attempt := 0; time.Now().Before(end); attempt++ { + resp, err = s.Do(r) + if err == nil { + return resp, err + } + DelayForBackoff(backoff, attempt, r.Cancel) + } + return resp, err + }) + } +} + +// WithLogging returns a SendDecorator that implements simple before and after logging of the +// request. +func WithLogging(logger *log.Logger) SendDecorator { + return func(s Sender) Sender { + return SenderFunc(func(r *http.Request) (*http.Response, error) { + logger.Printf("Sending %s %s", r.Method, r.URL) + resp, err := s.Do(r) + if err != nil { + logger.Printf("%s %s received error '%v'", r.Method, r.URL, err) + } else { + logger.Printf("%s %s received %s", r.Method, r.URL, resp.Status) + } + return resp, err + }) + } +} + +// DelayForBackoff invokes time.After for the supplied backoff duration raised to the power of +// passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set +// to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early, +// returns false. +// Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt +// count. +func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool { + select { + case <-time.After(time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second): + return true + case <-cancel: + return false + } +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/utility.go b/vendor/github.com/Azure/go-autorest/autorest/utility.go new file mode 100644 index 000000000..78067148b --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/utility.go @@ -0,0 +1,178 @@ +package autorest + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "fmt" + "io" + "net/url" + "reflect" + "sort" + "strings" +) + +// EncodedAs is a series of constants specifying various data encodings +type EncodedAs string + +const ( + // EncodedAsJSON states that data is encoded as JSON + EncodedAsJSON EncodedAs = "JSON" + + // EncodedAsXML states that data is encoded as Xml + EncodedAsXML EncodedAs = "XML" +) + +// Decoder defines the decoding method json.Decoder and xml.Decoder share +type Decoder interface { + Decode(v interface{}) error +} + +// NewDecoder creates a new decoder appropriate to the passed encoding. +// encodedAs specifies the type of encoding and r supplies the io.Reader containing the +// encoded data. +func NewDecoder(encodedAs EncodedAs, r io.Reader) Decoder { + if encodedAs == EncodedAsJSON { + return json.NewDecoder(r) + } else if encodedAs == EncodedAsXML { + return xml.NewDecoder(r) + } + return nil +} + +// CopyAndDecode decodes the data from the passed io.Reader while making a copy. Having a copy +// is especially useful if there is a chance the data will fail to decode. +// encodedAs specifies the expected encoding, r provides the io.Reader to the data, and v +// is the decoding destination. +func CopyAndDecode(encodedAs EncodedAs, r io.Reader, v interface{}) (bytes.Buffer, error) { + b := bytes.Buffer{} + return b, NewDecoder(encodedAs, io.TeeReader(r, &b)).Decode(v) +} + +// TeeReadCloser returns a ReadCloser that writes to w what it reads from rc. +// It utilizes io.TeeReader to copy the data read and has the same behavior when reading. +// Further, when it is closed, it ensures that rc is closed as well. +func TeeReadCloser(rc io.ReadCloser, w io.Writer) io.ReadCloser { + return &teeReadCloser{rc, io.TeeReader(rc, w)} +} + +type teeReadCloser struct { + rc io.ReadCloser + r io.Reader +} + +func (t *teeReadCloser) Read(p []byte) (int, error) { + return t.r.Read(p) +} + +func (t *teeReadCloser) Close() error { + return t.rc.Close() +} + +func containsInt(ints []int, n int) bool { + for _, i := range ints { + if i == n { + return true + } + } + return false +} + +func escapeValueStrings(m map[string]string) map[string]string { + for key, value := range m { + m[key] = url.QueryEscape(value) + } + return m +} + +func ensureValueStrings(mapOfInterface map[string]interface{}) map[string]string { + mapOfStrings := make(map[string]string) + for key, value := range mapOfInterface { + mapOfStrings[key] = ensureValueString(value) + } + return mapOfStrings +} + +func ensureValueString(value interface{}) string { + if value == nil { + return "" + } + switch v := value.(type) { + case string: + return v + case []byte: + return string(v) + default: + return fmt.Sprintf("%v", v) + } +} + +// MapToValues method converts map[string]interface{} to url.Values. +func MapToValues(m map[string]interface{}) url.Values { + v := url.Values{} + for key, value := range m { + x := reflect.ValueOf(value) + if x.Kind() == reflect.Array || x.Kind() == reflect.Slice { + for i := 0; i < x.Len(); i++ { + v.Add(key, ensureValueString(x.Index(i))) + } + } else { + v.Add(key, ensureValueString(value)) + } + } + return v +} + +// String method converts interface v to string. If interface is a list, it +// joins list elements using separator. +func String(v interface{}, sep ...string) string { + if len(sep) > 0 { + return ensureValueString(strings.Join(v.([]string), sep[0])) + } + return ensureValueString(v) +} + +// Encode method encodes url path and query parameters. +func Encode(location string, v interface{}, sep ...string) string { + s := String(v, sep...) + switch strings.ToLower(location) { + case "path": + return pathEscape(s) + case "query": + return queryEscape(s) + default: + return s + } +} + +func pathEscape(s string) string { + return strings.Replace(url.QueryEscape(s), "+", "%20", -1) +} + +func queryEscape(s string) string { + return url.QueryEscape(s) +} + +// This method is same as Encode() method of "net/url" go package, +// except it does not encode the query parameters because they +// already come encoded. It formats values map in query format (bar=foo&a=b). +func createQuery(v url.Values) string { + var buf bytes.Buffer + keys := make([]string, 0, len(v)) + for k := range v { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := v[k] + prefix := url.QueryEscape(k) + "=" + for _, v := range vs { + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(prefix) + buf.WriteString(v) + } + } + return buf.String() +} diff --git a/vendor/github.com/Azure/go-autorest/autorest/version.go b/vendor/github.com/Azure/go-autorest/autorest/version.go new file mode 100644 index 000000000..7a0bf9c9f --- /dev/null +++ b/vendor/github.com/Azure/go-autorest/autorest/version.go @@ -0,0 +1,23 @@ +package autorest + +import ( + "fmt" +) + +const ( + major = "7" + minor = "3" + patch = "0" + tag = "" + semVerFormat = "%s.%s.%s%s" +) + +var version string + +// Version returns the semantic version (see http://semver.org). +func Version() string { + if version == "" { + version = fmt.Sprintf(semVerFormat, major, minor, patch, tag) + } + return version +} diff --git a/vendor/github.com/dgrijalva/jwt-go/LICENSE b/vendor/github.com/dgrijalva/jwt-go/LICENSE new file mode 100644 index 000000000..df83a9c2f --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/LICENSE @@ -0,0 +1,8 @@ +Copyright (c) 2012 Dave Grijalva + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md b/vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md new file mode 100644 index 000000000..7fc1f793c --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md @@ -0,0 +1,97 @@ +## Migration Guide from v2 -> v3 + +Version 3 adds several new, frequently requested features. To do so, it introduces a few breaking changes. We've worked to keep these as minimal as possible. This guide explains the breaking changes and how you can quickly update your code. + +### `Token.Claims` is now an interface type + +The most requested feature from the 2.0 verison of this library was the ability to provide a custom type to the JSON parser for claims. This was implemented by introducing a new interface, `Claims`, to replace `map[string]interface{}`. We also included two concrete implementations of `Claims`: `MapClaims` and `StandardClaims`. + +`MapClaims` is an alias for `map[string]interface{}` with built in validation behavior. It is the default claims type when using `Parse`. The usage is unchanged except you must type cast the claims property. + +The old example for parsing a token looked like this.. + +```go + if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil { + fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) + } +``` + +is now directly mapped to... + +```go + if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil { + claims := token.Claims.(jwt.MapClaims) + fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"]) + } +``` + +`StandardClaims` is designed to be embedded in your custom type. You can supply a custom claims type with the new `ParseWithClaims` function. Here's an example of using a custom claims type. + +```go + type MyCustomClaims struct { + User string + *StandardClaims + } + + if token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, keyLookupFunc); err == nil { + claims := token.Claims.(*MyCustomClaims) + fmt.Printf("Token for user %v expires %v", claims.User, claims.StandardClaims.ExpiresAt) + } +``` + +### `ParseFromRequest` has been moved + +To keep this library focused on the tokens without becoming overburdened with complex request processing logic, `ParseFromRequest` and its new companion `ParseFromRequestWithClaims` have been moved to a subpackage, `request`. The method signatues have also been augmented to receive a new argument: `Extractor`. + +`Extractors` do the work of picking the token string out of a request. The interface is simple and composable. + +This simple parsing example: + +```go + if token, err := jwt.ParseFromRequest(tokenString, req, keyLookupFunc); err == nil { + fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) + } +``` + +is directly mapped to: + +```go + if token, err := request.ParseFromRequest(req, request.OAuth2Extractor, keyLookupFunc); err == nil { + claims := token.Claims.(jwt.MapClaims) + fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"]) + } +``` + +There are several concrete `Extractor` types provided for your convenience: + +* `HeaderExtractor` will search a list of headers until one contains content. +* `ArgumentExtractor` will search a list of keys in request query and form arguments until one contains content. +* `MultiExtractor` will try a list of `Extractors` in order until one returns content. +* `AuthorizationHeaderExtractor` will look in the `Authorization` header for a `Bearer` token. +* `OAuth2Extractor` searches the places an OAuth2 token would be specified (per the spec): `Authorization` header and `access_token` argument +* `PostExtractionFilter` wraps an `Extractor`, allowing you to process the content before it's parsed. A simple example is stripping the `Bearer ` text from a header + + +### RSA signing methods no longer accept `[]byte` keys + +Due to a [critical vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/), we've decided the convenience of accepting `[]byte` instead of `rsa.PublicKey` or `rsa.PrivateKey` isn't worth the risk of misuse. + +To replace this behavior, we've added two helper methods: `ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)` and `ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)`. These are just simple helpers for unpacking PEM encoded PKCS1 and PKCS8 keys. If your keys are encoded any other way, all you need to do is convert them to the `crypto/rsa` package's types. + +```go + func keyLookupFunc(*Token) (interface{}, error) { + // Don't forget to validate the alg is what you expect: + if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + + // Look up key + key, err := lookupPublicKey(token.Header["kid"]) + if err != nil { + return nil, err + } + + // Unpack key from PEM encoded PKCS8 + return jwt.ParseRSAPublicKeyFromPEM(key) + } +``` diff --git a/vendor/github.com/dgrijalva/jwt-go/README.md b/vendor/github.com/dgrijalva/jwt-go/README.md new file mode 100644 index 000000000..f48365faf --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/README.md @@ -0,0 +1,85 @@ +A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html) + +[](https://travis-ci.org/dgrijalva/jwt-go) + +**BREAKING CHANGES:*** Version 3.0.0 is here. It includes _a lot_ of changes including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. + +**NOTICE:** A vulnerability in JWT was [recently published](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). As this library doesn't force users to validate the `alg` is what they expected, it's possible your usage is effected. There will be an update soon to remedy this, and it will likey require backwards-incompatible changes to the API. In the short term, please make sure your implementation verifies the `alg` is what you expect. + + +## What the heck is a JWT? + +JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens. + +In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way. + +The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used. + +The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [the RFC](http://self-issued.info/docs/draft-jones-json-web-token.html) for information about reserved keys and the proper way to add your own. + +## What's in the box? + +This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. + +## Examples + +See [the project documentation](https://godoc.org/github.com/dgrijalva/jwt-go) for examples of usage: + +* [Simple example of parsing and validating a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac) +* [Simple example of building and signing a token](https://godoc.org/github.com/dgrijalva/jwt-go#example-New--Hmac) +* [Directory of Examples](https://godoc.org/github.com/dgrijalva/jwt-go#pkg-examples) + +## Extensions + +This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. + +Here's an example of an extension that integrates with the Google App Engine signing tools: https://github.com/someone1/gcp-jwt-go + +## Compliance + +This library was last reviewed to comply with [RTF 7519](http://www.rfc-editor.org/info/rfc7519) dated May 2015 with a few notable differences: + +* In order to protect against accidental use of [Unsecured JWTs](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#UnsecuredJWT), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key. + +## Project Status & Versioning + +This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason). + +This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases). + +While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v2`. It will do the right thing WRT semantic versioning. + +## Usage Tips + +### Signing vs Encryption + +A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data: + +* The author of the token was in the possession of the signing secret +* The data has not been modified since it was signed + +It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. + +### Choosing a Signing Method + +There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric. + +Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation. + +Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification. + +### JWT and OAuth + +It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication. + +Without going too far down the rabbit hole, here's a description of the interaction of these technologies: + +* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth. +* OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. +* Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL. + +## More + +Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go). + +The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in to documentation. diff --git a/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md b/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md new file mode 100644 index 000000000..b605b4509 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md @@ -0,0 +1,105 @@ +## `jwt-go` Version History + +#### 3.0.0 + +* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code + * Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods. + * `ParseFromRequest` has been moved to `request` subpackage and usage has changed + * The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims. +* Other Additions and Changes + * Added `Claims` interface type to allow users to decode the claims into a custom type + * Added `ParseWithClaims`, which takes a third argument of type `Claims`. Use this function instead of `Parse` if you have a custom type you'd like to decode into. + * Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage + * Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims` + * Added new interface type `Extractor`, which is used for extracting JWT strings from http requests. Used with `ParseFromRequest` and `ParseFromRequestWithClaims`. + * Added several new, more specific, validation errors to error type bitmask + * Moved examples from README to executable example files + * Signing method registry is now thread safe + * Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser) + +#### 2.7.0 + +This will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes. + +* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying +* Error text for expired tokens includes how long it's been expired +* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM` +* Documentation updates + +#### 2.6.0 + +* Exposed inner error within ValidationError +* Fixed validation errors when using UseJSONNumber flag +* Added several unit tests + +#### 2.5.0 + +* Added support for signing method none. You shouldn't use this. The API tries to make this clear. +* Updated/fixed some documentation +* Added more helpful error message when trying to parse tokens that begin with `BEARER ` + +#### 2.4.0 + +* Added new type, Parser, to allow for configuration of various parsing parameters + * You can now specify a list of valid signing methods. Anything outside this set will be rejected. + * You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON +* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go) +* Fixed some bugs with ECDSA parsing + +#### 2.3.0 + +* Added support for ECDSA signing methods +* Added support for RSA PSS signing methods (requires go v1.4) + +#### 2.2.0 + +* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic. + +#### 2.1.0 + +Backwards compatible API change that was missed in 2.0.0. + +* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte` + +#### 2.0.0 + +There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change. + +The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. + +It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`. + +* **Compatibility Breaking Changes** + * `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct` + * `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct` + * `KeyFunc` now returns `interface{}` instead of `[]byte` + * `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key + * `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key +* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type. + * Added public package global `SigningMethodHS256` + * Added public package global `SigningMethodHS384` + * Added public package global `SigningMethodHS512` +* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type. + * Added public package global `SigningMethodRS256` + * Added public package global `SigningMethodRS384` + * Added public package global `SigningMethodRS512` +* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged. +* Refactored the RSA implementation to be easier to read +* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM` + +#### 1.0.2 + +* Fixed bug in parsing public keys from certificates +* Added more tests around the parsing of keys for RS256 +* Code refactoring in RS256 implementation. No functional changes + +#### 1.0.1 + +* Fixed panic if RS256 signing method was passed an invalid key + +#### 1.0.0 + +* First versioned release +* API stabilized +* Supports creating, signing, parsing, and validating JWT tokens +* Supports RS256 and HS256 signing methods
\ No newline at end of file diff --git a/vendor/github.com/dgrijalva/jwt-go/claims.go b/vendor/github.com/dgrijalva/jwt-go/claims.go new file mode 100644 index 000000000..f0228f02e --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/claims.go @@ -0,0 +1,134 @@ +package jwt + +import ( + "crypto/subtle" + "fmt" + "time" +) + +// For a type to be a Claims object, it must just have a Valid method that determines +// if the token is invalid for any supported reason +type Claims interface { + Valid() error +} + +// Structured version of Claims Section, as referenced at +// https://tools.ietf.org/html/rfc7519#section-4.1 +// See examples for how to use this with your own claim types +type StandardClaims struct { + Audience string `json:"aud,omitempty"` + ExpiresAt int64 `json:"exp,omitempty"` + Id string `json:"jti,omitempty"` + IssuedAt int64 `json:"iat,omitempty"` + Issuer string `json:"iss,omitempty"` + NotBefore int64 `json:"nbf,omitempty"` + Subject string `json:"sub,omitempty"` +} + +// Validates time based claims "exp, iat, nbf". +// There is no accounting for clock skew. +// As well, if any of the above claims are not in the token, it will still +// be considered a valid claim. +func (c StandardClaims) Valid() error { + vErr := new(ValidationError) + now := TimeFunc().Unix() + + // The claims below are optional, by default, so if they are set to the + // default value in Go, let's not fail the verification for them. + if c.VerifyExpiresAt(now, false) == false { + delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) + vErr.Inner = fmt.Errorf("token is expired by %v", delta) + vErr.Errors |= ValidationErrorExpired + } + + if c.VerifyIssuedAt(now, false) == false { + vErr.Inner = fmt.Errorf("Token used before issued") + vErr.Errors |= ValidationErrorIssuedAt + } + + if c.VerifyNotBefore(now, false) == false { + vErr.Inner = fmt.Errorf("token is not valid yet") + vErr.Errors |= ValidationErrorNotValidYet + } + + if vErr.valid() { + return nil + } + + return vErr +} + +// Compares the aud claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { + return verifyAud(c.Audience, cmp, req) +} + +// Compares the exp claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { + return verifyExp(c.ExpiresAt, cmp, req) +} + +// Compares the iat claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { + return verifyIat(c.IssuedAt, cmp, req) +} + +// Compares the iss claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { + return verifyIss(c.Issuer, cmp, req) +} + +// Compares the nbf claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { + return verifyNbf(c.NotBefore, cmp, req) +} + +// ----- helpers + +func verifyAud(aud string, cmp string, required bool) bool { + if aud == "" { + return !required + } + if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 { + return true + } else { + return false + } +} + +func verifyExp(exp int64, now int64, required bool) bool { + if exp == 0 { + return !required + } + return now <= exp +} + +func verifyIat(iat int64, now int64, required bool) bool { + if iat == 0 { + return !required + } + return now >= iat +} + +func verifyIss(iss string, cmp string, required bool) bool { + if iss == "" { + return !required + } + if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { + return true + } else { + return false + } +} + +func verifyNbf(nbf int64, now int64, required bool) bool { + if nbf == 0 { + return !required + } + return now >= nbf +} diff --git a/vendor/github.com/dgrijalva/jwt-go/doc.go b/vendor/github.com/dgrijalva/jwt-go/doc.go new file mode 100644 index 000000000..a86dc1a3b --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/doc.go @@ -0,0 +1,4 @@ +// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html +// +// See README.md for more info. +package jwt diff --git a/vendor/github.com/dgrijalva/jwt-go/ecdsa.go b/vendor/github.com/dgrijalva/jwt-go/ecdsa.go new file mode 100644 index 000000000..2f59a2223 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/ecdsa.go @@ -0,0 +1,147 @@ +package jwt + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + "errors" + "math/big" +) + +var ( + // Sadly this is missing from crypto/ecdsa compared to crypto/rsa + ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") +) + +// Implements the ECDSA family of signing methods signing methods +type SigningMethodECDSA struct { + Name string + Hash crypto.Hash + KeySize int + CurveBits int +} + +// Specific instances for EC256 and company +var ( + SigningMethodES256 *SigningMethodECDSA + SigningMethodES384 *SigningMethodECDSA + SigningMethodES512 *SigningMethodECDSA +) + +func init() { + // ES256 + SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} + RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { + return SigningMethodES256 + }) + + // ES384 + SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} + RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { + return SigningMethodES384 + }) + + // ES512 + SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} + RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { + return SigningMethodES512 + }) +} + +func (m *SigningMethodECDSA) Alg() string { + return m.Name +} + +// Implements the Verify method from SigningMethod +// For this verify method, key must be an ecdsa.PublicKey struct +func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + // Get the key + var ecdsaKey *ecdsa.PublicKey + switch k := key.(type) { + case *ecdsa.PublicKey: + ecdsaKey = k + default: + return ErrInvalidKeyType + } + + if len(sig) != 2*m.KeySize { + return ErrECDSAVerification + } + + r := big.NewInt(0).SetBytes(sig[:m.KeySize]) + s := big.NewInt(0).SetBytes(sig[m.KeySize:]) + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Verify the signature + if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true { + return nil + } else { + return ErrECDSAVerification + } +} + +// Implements the Sign method from SigningMethod +// For this signing method, key must be an ecdsa.PrivateKey struct +func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { + // Get the key + var ecdsaKey *ecdsa.PrivateKey + switch k := key.(type) { + case *ecdsa.PrivateKey: + ecdsaKey = k + default: + return "", ErrInvalidKeyType + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return r, s + if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { + curveBits := ecdsaKey.Curve.Params().BitSize + + if m.CurveBits != curveBits { + return "", ErrInvalidKey + } + + keyBytes := curveBits / 8 + if curveBits%8 > 0 { + keyBytes += 1 + } + + // We serialize the outpus (r and s) into big-endian byte arrays and pad + // them with zeros on the left to make sure the sizes work out. Both arrays + // must be keyBytes long, and the output must be 2*keyBytes long. + rBytes := r.Bytes() + rBytesPadded := make([]byte, keyBytes) + copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) + + sBytes := s.Bytes() + sBytesPadded := make([]byte, keyBytes) + copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) + + out := append(rBytesPadded, sBytesPadded...) + + return EncodeSegment(out), nil + } else { + return "", err + } +} diff --git a/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go b/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go new file mode 100644 index 000000000..d19624b72 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go @@ -0,0 +1,67 @@ +package jwt + +import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key") + ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key") +) + +// Parse PEM encoded Elliptic Curve Private Key Structure +func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { + return nil, err + } + + var pkey *ecdsa.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { + return nil, ErrNotECPrivateKey + } + + return pkey, nil +} + +// Parse PEM encoded PKCS1 or PKCS8 public key +func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + if cert, err := x509.ParseCertificate(block.Bytes); err == nil { + parsedKey = cert.PublicKey + } else { + return nil, err + } + } + + var pkey *ecdsa.PublicKey + var ok bool + if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok { + return nil, ErrNotECPublicKey + } + + return pkey, nil +} diff --git a/vendor/github.com/dgrijalva/jwt-go/errors.go b/vendor/github.com/dgrijalva/jwt-go/errors.go new file mode 100644 index 000000000..1c93024aa --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/errors.go @@ -0,0 +1,59 @@ +package jwt + +import ( + "errors" +) + +// Error constants +var ( + ErrInvalidKey = errors.New("key is invalid") + ErrInvalidKeyType = errors.New("key is of invalid type") + ErrHashUnavailable = errors.New("the requested hash function is unavailable") +) + +// The errors that might occur when parsing and validating a token +const ( + ValidationErrorMalformed uint32 = 1 << iota // Token is malformed + ValidationErrorUnverifiable // Token could not be verified because of signing problems + ValidationErrorSignatureInvalid // Signature validation failed + + // Standard Claim validation errors + ValidationErrorAudience // AUD validation failed + ValidationErrorExpired // EXP validation failed + ValidationErrorIssuedAt // IAT validation failed + ValidationErrorIssuer // ISS validation failed + ValidationErrorNotValidYet // NBF validation failed + ValidationErrorId // JTI validation failed + ValidationErrorClaimsInvalid // Generic claims validation error +) + +// Helper for constructing a ValidationError with a string error message +func NewValidationError(errorText string, errorFlags uint32) *ValidationError { + return &ValidationError{ + text: errorText, + Errors: errorFlags, + } +} + +// The error from Parse if token is not valid +type ValidationError struct { + Inner error // stores the error returned by external dependencies, i.e.: KeyFunc + Errors uint32 // bitfield. see ValidationError... constants + text string // errors that do not have a valid error just have text +} + +// Validation error is an error type +func (e ValidationError) Error() string { + if e.Inner != nil { + return e.Inner.Error() + } else if e.text != "" { + return e.text + } else { + return "token is invalid" + } +} + +// No errors +func (e *ValidationError) valid() bool { + return e.Errors == 0 +} diff --git a/vendor/github.com/dgrijalva/jwt-go/hmac.go b/vendor/github.com/dgrijalva/jwt-go/hmac.go new file mode 100644 index 000000000..c22991925 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/hmac.go @@ -0,0 +1,94 @@ +package jwt + +import ( + "crypto" + "crypto/hmac" + "errors" +) + +// Implements the HMAC-SHA family of signing methods signing methods +type SigningMethodHMAC struct { + Name string + Hash crypto.Hash +} + +// Specific instances for HS256 and company +var ( + SigningMethodHS256 *SigningMethodHMAC + SigningMethodHS384 *SigningMethodHMAC + SigningMethodHS512 *SigningMethodHMAC + ErrSignatureInvalid = errors.New("signature is invalid") +) + +func init() { + // HS256 + SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256} + RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod { + return SigningMethodHS256 + }) + + // HS384 + SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384} + RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod { + return SigningMethodHS384 + }) + + // HS512 + SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512} + RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod { + return SigningMethodHS512 + }) +} + +func (m *SigningMethodHMAC) Alg() string { + return m.Name +} + +// Verify the signature of HSXXX tokens. Returns nil if the signature is valid. +func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error { + // Verify the key is the right type + keyBytes, ok := key.([]byte) + if !ok { + return ErrInvalidKeyType + } + + // Decode signature, for comparison + sig, err := DecodeSegment(signature) + if err != nil { + return err + } + + // Can we use the specified hashing method? + if !m.Hash.Available() { + return ErrHashUnavailable + } + + // This signing method is symmetric, so we validate the signature + // by reproducing the signature from the signing string and key, then + // comparing that against the provided signature. + hasher := hmac.New(m.Hash.New, keyBytes) + hasher.Write([]byte(signingString)) + if !hmac.Equal(sig, hasher.Sum(nil)) { + return ErrSignatureInvalid + } + + // No validation errors. Signature is good. + return nil +} + +// Implements the Sign method from SigningMethod for this signing method. +// Key must be []byte +func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) { + if keyBytes, ok := key.([]byte); ok { + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := hmac.New(m.Hash.New, keyBytes) + hasher.Write([]byte(signingString)) + + return EncodeSegment(hasher.Sum(nil)), nil + } + + return "", ErrInvalidKey +} diff --git a/vendor/github.com/dgrijalva/jwt-go/map_claims.go b/vendor/github.com/dgrijalva/jwt-go/map_claims.go new file mode 100644 index 000000000..291213c46 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/map_claims.go @@ -0,0 +1,94 @@ +package jwt + +import ( + "encoding/json" + "errors" + // "fmt" +) + +// Claims type that uses the map[string]interface{} for JSON decoding +// This is the default claims type if you don't supply one +type MapClaims map[string]interface{} + +// Compares the aud claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyAudience(cmp string, req bool) bool { + aud, _ := m["aud"].(string) + return verifyAud(aud, cmp, req) +} + +// Compares the exp claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { + switch exp := m["exp"].(type) { + case float64: + return verifyExp(int64(exp), cmp, req) + case json.Number: + v, _ := exp.Int64() + return verifyExp(v, cmp, req) + } + return req == false +} + +// Compares the iat claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { + switch iat := m["iat"].(type) { + case float64: + return verifyIat(int64(iat), cmp, req) + case json.Number: + v, _ := iat.Int64() + return verifyIat(v, cmp, req) + } + return req == false +} + +// Compares the iss claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyIssuer(cmp string, req bool) bool { + iss, _ := m["iss"].(string) + return verifyIss(iss, cmp, req) +} + +// Compares the nbf claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { + switch nbf := m["nbf"].(type) { + case float64: + return verifyNbf(int64(nbf), cmp, req) + case json.Number: + v, _ := nbf.Int64() + return verifyNbf(v, cmp, req) + } + return req == false +} + +// Validates time based claims "exp, iat, nbf". +// There is no accounting for clock skew. +// As well, if any of the above claims are not in the token, it will still +// be considered a valid claim. +func (m MapClaims) Valid() error { + vErr := new(ValidationError) + now := TimeFunc().Unix() + + if m.VerifyExpiresAt(now, false) == false { + vErr.Inner = errors.New("Token is expired") + vErr.Errors |= ValidationErrorExpired + } + + if m.VerifyIssuedAt(now, false) == false { + vErr.Inner = errors.New("Token used before issued") + vErr.Errors |= ValidationErrorIssuedAt + } + + if m.VerifyNotBefore(now, false) == false { + vErr.Inner = errors.New("Token is not valid yet") + vErr.Errors |= ValidationErrorNotValidYet + } + + if vErr.valid() { + return nil + } + + return vErr +} diff --git a/vendor/github.com/dgrijalva/jwt-go/none.go b/vendor/github.com/dgrijalva/jwt-go/none.go new file mode 100644 index 000000000..f04d189d0 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/none.go @@ -0,0 +1,52 @@ +package jwt + +// Implements the none signing method. This is required by the spec +// but you probably should never use it. +var SigningMethodNone *signingMethodNone + +const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed" + +var NoneSignatureTypeDisallowedError error + +type signingMethodNone struct{} +type unsafeNoneMagicConstant string + +func init() { + SigningMethodNone = &signingMethodNone{} + NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid) + + RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod { + return SigningMethodNone + }) +} + +func (m *signingMethodNone) Alg() string { + return "none" +} + +// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key +func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) { + // Key must be UnsafeAllowNoneSignatureType to prevent accidentally + // accepting 'none' signing method + if _, ok := key.(unsafeNoneMagicConstant); !ok { + return NoneSignatureTypeDisallowedError + } + // If signing method is none, signature must be an empty string + if signature != "" { + return NewValidationError( + "'none' signing method with non-empty signature", + ValidationErrorSignatureInvalid, + ) + } + + // Accept 'none' signing method. + return nil +} + +// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key +func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) { + if _, ok := key.(unsafeNoneMagicConstant); ok { + return "", nil + } + return "", NoneSignatureTypeDisallowedError +} diff --git a/vendor/github.com/dgrijalva/jwt-go/parser.go b/vendor/github.com/dgrijalva/jwt-go/parser.go new file mode 100644 index 000000000..7bf1c4ea0 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/parser.go @@ -0,0 +1,131 @@ +package jwt + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" +) + +type Parser struct { + ValidMethods []string // If populated, only these methods will be considered valid + UseJSONNumber bool // Use JSON Number format in JSON decoder + SkipClaimsValidation bool // Skip claims validation during token parsing +} + +// Parse, validate, and return a token. +// keyFunc will receive the parsed token and should return the key for validating. +// If everything is kosher, err will be nil +func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { + return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) +} + +func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { + parts := strings.Split(tokenString, ".") + if len(parts) != 3 { + return nil, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) + } + + var err error + token := &Token{Raw: tokenString} + + // parse Header + var headerBytes []byte + if headerBytes, err = DecodeSegment(parts[0]); err != nil { + if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") { + return token, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed) + } + return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + if err = json.Unmarshal(headerBytes, &token.Header); err != nil { + return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + + // parse Claims + var claimBytes []byte + token.Claims = claims + + if claimBytes, err = DecodeSegment(parts[1]); err != nil { + return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) + if p.UseJSONNumber { + dec.UseNumber() + } + // JSON Decode. Special case for map type to avoid weird pointer behavior + if c, ok := token.Claims.(MapClaims); ok { + err = dec.Decode(&c) + } else { + err = dec.Decode(&claims) + } + // Handle decode error + if err != nil { + return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + + // Lookup signature method + if method, ok := token.Header["alg"].(string); ok { + if token.Method = GetSigningMethod(method); token.Method == nil { + return token, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable) + } + } else { + return token, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable) + } + + // Verify signing method is in the required set + if p.ValidMethods != nil { + var signingMethodValid = false + var alg = token.Method.Alg() + for _, m := range p.ValidMethods { + if m == alg { + signingMethodValid = true + break + } + } + if !signingMethodValid { + // signing method is not in the listed set + return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid) + } + } + + // Lookup key + var key interface{} + if keyFunc == nil { + // keyFunc was not provided. short circuiting validation + return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable) + } + if key, err = keyFunc(token); err != nil { + // keyFunc returned an error + return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} + } + + vErr := &ValidationError{} + + // Validate Claims + if !p.SkipClaimsValidation { + if err := token.Claims.Valid(); err != nil { + + // If the Claims Valid returned an error, check if it is a validation error, + // If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set + if e, ok := err.(*ValidationError); !ok { + vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid} + } else { + vErr = e + } + } + } + + // Perform validation + token.Signature = parts[2] + if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { + vErr.Inner = err + vErr.Errors |= ValidationErrorSignatureInvalid + } + + if vErr.valid() { + token.Valid = true + return token, nil + } + + return token, vErr +} diff --git a/vendor/github.com/dgrijalva/jwt-go/rsa.go b/vendor/github.com/dgrijalva/jwt-go/rsa.go new file mode 100644 index 000000000..0ae0b1984 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/rsa.go @@ -0,0 +1,100 @@ +package jwt + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" +) + +// Implements the RSA family of signing methods signing methods +type SigningMethodRSA struct { + Name string + Hash crypto.Hash +} + +// Specific instances for RS256 and company +var ( + SigningMethodRS256 *SigningMethodRSA + SigningMethodRS384 *SigningMethodRSA + SigningMethodRS512 *SigningMethodRSA +) + +func init() { + // RS256 + SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256} + RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod { + return SigningMethodRS256 + }) + + // RS384 + SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384} + RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod { + return SigningMethodRS384 + }) + + // RS512 + SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512} + RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod { + return SigningMethodRS512 + }) +} + +func (m *SigningMethodRSA) Alg() string { + return m.Name +} + +// Implements the Verify method from SigningMethod +// For this signing method, must be an rsa.PublicKey structure. +func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + var rsaKey *rsa.PublicKey + var ok bool + + if rsaKey, ok = key.(*rsa.PublicKey); !ok { + return ErrInvalidKeyType + } + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Verify the signature + return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig) +} + +// Implements the Sign method from SigningMethod +// For this signing method, must be an rsa.PrivateKey structure. +func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { + var rsaKey *rsa.PrivateKey + var ok bool + + // Validate type of key + if rsaKey, ok = key.(*rsa.PrivateKey); !ok { + return "", ErrInvalidKey + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return the encoded bytes + if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil { + return EncodeSegment(sigBytes), nil + } else { + return "", err + } +} diff --git a/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go b/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go new file mode 100644 index 000000000..10ee9db8a --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go @@ -0,0 +1,126 @@ +// +build go1.4 + +package jwt + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" +) + +// Implements the RSAPSS family of signing methods signing methods +type SigningMethodRSAPSS struct { + *SigningMethodRSA + Options *rsa.PSSOptions +} + +// Specific instances for RS/PS and company +var ( + SigningMethodPS256 *SigningMethodRSAPSS + SigningMethodPS384 *SigningMethodRSAPSS + SigningMethodPS512 *SigningMethodRSAPSS +) + +func init() { + // PS256 + SigningMethodPS256 = &SigningMethodRSAPSS{ + &SigningMethodRSA{ + Name: "PS256", + Hash: crypto.SHA256, + }, + &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA256, + }, + } + RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { + return SigningMethodPS256 + }) + + // PS384 + SigningMethodPS384 = &SigningMethodRSAPSS{ + &SigningMethodRSA{ + Name: "PS384", + Hash: crypto.SHA384, + }, + &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA384, + }, + } + RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { + return SigningMethodPS384 + }) + + // PS512 + SigningMethodPS512 = &SigningMethodRSAPSS{ + &SigningMethodRSA{ + Name: "PS512", + Hash: crypto.SHA512, + }, + &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA512, + }, + } + RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { + return SigningMethodPS512 + }) +} + +// Implements the Verify method from SigningMethod +// For this verify method, key must be an rsa.PublicKey struct +func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + var rsaKey *rsa.PublicKey + switch k := key.(type) { + case *rsa.PublicKey: + rsaKey = k + default: + return ErrInvalidKey + } + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options) +} + +// Implements the Sign method from SigningMethod +// For this signing method, key must be an rsa.PrivateKey struct +func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { + var rsaKey *rsa.PrivateKey + + switch k := key.(type) { + case *rsa.PrivateKey: + rsaKey = k + default: + return "", ErrInvalidKeyType + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return the encoded bytes + if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil { + return EncodeSegment(sigBytes), nil + } else { + return "", err + } +} diff --git a/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go b/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go new file mode 100644 index 000000000..213a90dbb --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go @@ -0,0 +1,69 @@ +package jwt + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key") + ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key") + ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key") +) + +// Parse PEM encoded PKCS1 or PKCS8 private key +func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + var parsedKey interface{} + if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { + if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + } + + var pkey *rsa.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { + return nil, ErrNotRSAPrivateKey + } + + return pkey, nil +} + +// Parse PEM encoded PKCS1 or PKCS8 public key +func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + if cert, err := x509.ParseCertificate(block.Bytes); err == nil { + parsedKey = cert.PublicKey + } else { + return nil, err + } + } + + var pkey *rsa.PublicKey + var ok bool + if pkey, ok = parsedKey.(*rsa.PublicKey); !ok { + return nil, ErrNotRSAPublicKey + } + + return pkey, nil +} diff --git a/vendor/github.com/dgrijalva/jwt-go/signing_method.go b/vendor/github.com/dgrijalva/jwt-go/signing_method.go new file mode 100644 index 000000000..ed1f212b2 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/signing_method.go @@ -0,0 +1,35 @@ +package jwt + +import ( + "sync" +) + +var signingMethods = map[string]func() SigningMethod{} +var signingMethodLock = new(sync.RWMutex) + +// Implement SigningMethod to add new methods for signing or verifying tokens. +type SigningMethod interface { + Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid + Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error + Alg() string // returns the alg identifier for this method (example: 'HS256') +} + +// Register the "alg" name and a factory function for signing method. +// This is typically done during init() in the method's implementation +func RegisterSigningMethod(alg string, f func() SigningMethod) { + signingMethodLock.Lock() + defer signingMethodLock.Unlock() + + signingMethods[alg] = f +} + +// Get a signing method from an "alg" string +func GetSigningMethod(alg string) (method SigningMethod) { + signingMethodLock.RLock() + defer signingMethodLock.RUnlock() + + if methodF, ok := signingMethods[alg]; ok { + method = methodF() + } + return +} diff --git a/vendor/github.com/dgrijalva/jwt-go/token.go b/vendor/github.com/dgrijalva/jwt-go/token.go new file mode 100644 index 000000000..d637e0867 --- /dev/null +++ b/vendor/github.com/dgrijalva/jwt-go/token.go @@ -0,0 +1,108 @@ +package jwt + +import ( + "encoding/base64" + "encoding/json" + "strings" + "time" +) + +// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time). +// You can override it to use another time value. This is useful for testing or if your +// server uses a different time zone than your tokens. +var TimeFunc = time.Now + +// Parse methods use this callback function to supply +// the key for verification. The function receives the parsed, +// but unverified Token. This allows you to use properties in the +// Header of the token (such as `kid`) to identify which key to use. +type Keyfunc func(*Token) (interface{}, error) + +// A JWT Token. Different fields will be used depending on whether you're +// creating or parsing/verifying a token. +type Token struct { + Raw string // The raw token. Populated when you Parse a token + Method SigningMethod // The signing method used or to be used + Header map[string]interface{} // The first segment of the token + Claims Claims // The second segment of the token + Signature string // The third segment of the token. Populated when you Parse a token + Valid bool // Is the token valid? Populated when you Parse/Verify a token +} + +// Create a new Token. Takes a signing method +func New(method SigningMethod) *Token { + return NewWithClaims(method, MapClaims{}) +} + +func NewWithClaims(method SigningMethod, claims Claims) *Token { + return &Token{ + Header: map[string]interface{}{ + "typ": "JWT", + "alg": method.Alg(), + }, + Claims: claims, + Method: method, + } +} + +// Get the complete, signed token +func (t *Token) SignedString(key interface{}) (string, error) { + var sig, sstr string + var err error + if sstr, err = t.SigningString(); err != nil { + return "", err + } + if sig, err = t.Method.Sign(sstr, key); err != nil { + return "", err + } + return strings.Join([]string{sstr, sig}, "."), nil +} + +// Generate the signing string. This is the +// most expensive part of the whole deal. Unless you +// need this for something special, just go straight for +// the SignedString. +func (t *Token) SigningString() (string, error) { + var err error + parts := make([]string, 2) + for i, _ := range parts { + var jsonValue []byte + if i == 0 { + if jsonValue, err = json.Marshal(t.Header); err != nil { + return "", err + } + } else { + if jsonValue, err = json.Marshal(t.Claims); err != nil { + return "", err + } + } + + parts[i] = EncodeSegment(jsonValue) + } + return strings.Join(parts, "."), nil +} + +// Parse, validate, and return a token. +// keyFunc will receive the parsed token and should return the key for validating. +// If everything is kosher, err will be nil +func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { + return new(Parser).Parse(tokenString, keyFunc) +} + +func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { + return new(Parser).ParseWithClaims(tokenString, claims, keyFunc) +} + +// Encode JWT specific base64url encoding with padding stripped +func EncodeSegment(seg []byte) string { + return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=") +} + +// Decode JWT specific base64url encoding with padding stripped +func DecodeSegment(seg string) ([]byte, error) { + if l := len(seg) % 4; l > 0 { + seg += strings.Repeat("=", 4-l) + } + + return base64.URLEncoding.DecodeString(seg) +} diff --git a/vendor/github.com/naoina/go-stringutil/LICENSE b/vendor/github.com/naoina/go-stringutil/LICENSE new file mode 100644 index 000000000..0fff1c58b --- /dev/null +++ b/vendor/github.com/naoina/go-stringutil/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Naoya Inada <naoina@kuune.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/naoina/go-stringutil/README.md b/vendor/github.com/naoina/go-stringutil/README.md new file mode 100644 index 000000000..ecf7a5fae --- /dev/null +++ b/vendor/github.com/naoina/go-stringutil/README.md @@ -0,0 +1,13 @@ +# stringutil [](https://travis-ci.org/naoina/go-stringutil) + +## Installation + + go get -u github.com/naoina/go-stringutil + +## Documentation + +See https://godoc.org/github.com/naoina/go-stringutil + +## License + +MIT diff --git a/vendor/github.com/naoina/go-stringutil/da.go b/vendor/github.com/naoina/go-stringutil/da.go new file mode 100644 index 000000000..8fe651659 --- /dev/null +++ b/vendor/github.com/naoina/go-stringutil/da.go @@ -0,0 +1,253 @@ +package stringutil + +import ( + "fmt" + "sort" + "unicode/utf8" +) + +const ( + terminationCharacter = '#' +) + +func mustDoubleArray(da *doubleArray, err error) *doubleArray { + if err != nil { + panic(err) + } + return da +} + +func (da *doubleArray) Build(keys []string) error { + records := makeRecords(keys) + if err := da.build(records, 1, 0, make(map[int]struct{})); err != nil { + return err + } + return nil +} + +type doubleArray struct { + bc []baseCheck + node []int +} + +func newDoubleArray(keys []string) (*doubleArray, error) { + da := &doubleArray{ + bc: []baseCheck{0}, + node: []int{-1}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node. + } + if err := da.Build(keys); err != nil { + return nil, err + } + return da, nil +} + +// baseCheck contains BASE, CHECK and Extra flags. +// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK. +// +// BASE (22bit) | Extra flags (2bit) | CHECK (8bit) +// |----------------------|--|--------| +// 32 10 8 0 +type baseCheck uint32 + +func (bc baseCheck) Base() int { + return int(bc >> 10) +} + +func (bc *baseCheck) SetBase(base int) { + *bc |= baseCheck(base) << 10 +} + +func (bc baseCheck) Check() byte { + return byte(bc) +} + +func (bc *baseCheck) SetCheck(check byte) { + *bc |= baseCheck(check) +} + +func (bc baseCheck) IsEmpty() bool { + return bc&0xfffffcff == 0 +} + +func (da *doubleArray) Lookup(path string) (length int) { + idx := 1 + tmpIdx := idx + for i := 0; i < len(path); i++ { + c := path[i] + tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c) + if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c { + break + } + idx = tmpIdx + } + if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter { + return da.node[da.bc[next].Base()] + } + return -1 +} + +func (da *doubleArray) LookupByBytes(path []byte) (length int) { + idx := 1 + tmpIdx := idx + for i := 0; i < len(path); i++ { + c := path[i] + tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c) + if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c { + break + } + idx = tmpIdx + } + if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter { + return da.node[da.bc[next].Base()] + } + return -1 +} + +func (da *doubleArray) build(srcs []record, idx, depth int, usedBase map[int]struct{}) error { + sort.Stable(recordSlice(srcs)) + base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase) + if err != nil { + return err + } + if leaf != nil { + da.bc[idx].SetBase(len(da.node)) + da.node = append(da.node, leaf.value) + } + for _, sib := range siblings { + da.setCheck(da.nextIndex(base, sib.c), sib.c) + } + for _, sib := range siblings { + if err := da.build(srcs[sib.start:sib.end], da.nextIndex(base, sib.c), depth+1, usedBase); err != nil { + return err + } + } + return nil +} + +func (da *doubleArray) setBase(i, base int) { + da.bc[i].SetBase(base) +} + +func (da *doubleArray) setCheck(i int, check byte) { + da.bc[i].SetCheck(check) +} + +func (da *doubleArray) findEmptyIndex(start int) int { + i := start + for ; i < len(da.bc); i++ { + if da.bc[i].IsEmpty() { + break + } + } + return i +} + +// findBase returns good BASE. +func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) { + for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) { + base = da.nextIndex(idx, firstChar) + if _, used := usedBase[base]; used { + continue + } + i := 0 + for ; i < len(siblings); i++ { + next := da.nextIndex(base, siblings[i].c) + if len(da.bc) <= next { + da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...) + } + if !da.bc[next].IsEmpty() { + break + } + } + if i == len(siblings) { + break + } + } + usedBase[base] = struct{}{} + return base +} + +func (da *doubleArray) arrange(records []record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) { + siblings, leaf, err = makeSiblings(records, depth) + if err != nil { + return -1, nil, nil, err + } + if len(siblings) < 1 { + return -1, nil, leaf, nil + } + base = da.findBase(siblings, idx, usedBase) + da.setBase(idx, base) + return base, siblings, leaf, err +} + +type sibling struct { + start int + end int + c byte +} + +func (da *doubleArray) nextIndex(base int, c byte) int { + return base ^ int(c) +} + +func makeSiblings(records []record, depth int) (sib []sibling, leaf *record, err error) { + var ( + pc byte + n int + ) + for i, r := range records { + if len(r.key) <= depth { + leaf = &r + continue + } + c := r.key[depth] + switch { + case pc < c: + sib = append(sib, sibling{start: i, c: c}) + case pc == c: + continue + default: + return nil, nil, fmt.Errorf("stringutil: BUG: records hasn't been sorted") + } + if n > 0 { + sib[n-1].end = i + } + pc = c + n++ + } + if n == 0 { + return nil, leaf, nil + } + sib[n-1].end = len(records) + return sib, leaf, nil +} + +type record struct { + key string + value int +} + +func makeRecords(srcs []string) (records []record) { + termChar := string(terminationCharacter) + for _, s := range srcs { + records = append(records, record{ + key: string(s + termChar), + value: utf8.RuneCountInString(s), + }) + } + return records +} + +type recordSlice []record + +func (rs recordSlice) Len() int { + return len(rs) +} + +func (rs recordSlice) Less(i, j int) bool { + return rs[i].key < rs[j].key +} + +func (rs recordSlice) Swap(i, j int) { + rs[i], rs[j] = rs[j], rs[i] +} diff --git a/vendor/github.com/naoina/go-stringutil/strings.go b/vendor/github.com/naoina/go-stringutil/strings.go new file mode 100644 index 000000000..881ca2c8f --- /dev/null +++ b/vendor/github.com/naoina/go-stringutil/strings.go @@ -0,0 +1,320 @@ +package stringutil + +import ( + "sync" + "unicode" + "unicode/utf8" +) + +var ( + mu sync.Mutex + + // Based on https://github.com/golang/lint/blob/32a87160691b3c96046c0c678fe57c5bef761456/lint.go#L702 + commonInitialismMap = map[string]struct{}{ + "API": struct{}{}, + "ASCII": struct{}{}, + "CPU": struct{}{}, + "CSRF": struct{}{}, + "CSS": struct{}{}, + "DNS": struct{}{}, + "EOF": struct{}{}, + "GUID": struct{}{}, + "HTML": struct{}{}, + "HTTP": struct{}{}, + "HTTPS": struct{}{}, + "ID": struct{}{}, + "IP": struct{}{}, + "JSON": struct{}{}, + "LHS": struct{}{}, + "QPS": struct{}{}, + "RAM": struct{}{}, + "RHS": struct{}{}, + "RPC": struct{}{}, + "SLA": struct{}{}, + "SMTP": struct{}{}, + "SQL": struct{}{}, + "SSH": struct{}{}, + "TCP": struct{}{}, + "TLS": struct{}{}, + "TTL": struct{}{}, + "UDP": struct{}{}, + "UI": struct{}{}, + "UID": struct{}{}, + "UUID": struct{}{}, + "URI": struct{}{}, + "URL": struct{}{}, + "UTF8": struct{}{}, + "VM": struct{}{}, + "XML": struct{}{}, + "XSRF": struct{}{}, + "XSS": struct{}{}, + } + commonInitialisms = keys(commonInitialismMap) + commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) + longestLen = longestLength(commonInitialisms) + shortestLen = shortestLength(commonInitialisms, longestLen) +) + +// ToUpperCamelCase returns a copy of the string s with all Unicode letters mapped to their camel case. +// It will convert to upper case previous letter of '_' and first letter, and remove letter of '_'. +func ToUpperCamelCase(s string) string { + if s == "" { + return "" + } + upper := true + start := 0 + result := make([]byte, 0, len(s)) + var runeBuf [utf8.UTFMax]byte + var initialism []byte + for _, c := range s { + if c == '_' { + upper = true + candidate := string(result[start:]) + initialism = initialism[:0] + for _, r := range candidate { + if r < utf8.RuneSelf { + initialism = append(initialism, toUpperASCII(byte(r))) + } else { + n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r)) + initialism = append(initialism, runeBuf[:n]...) + } + } + if length := commonInitialism.LookupByBytes(initialism); length > 0 { + result = append(result[:start], initialism...) + } + start = len(result) + continue + } + if upper { + if c < utf8.RuneSelf { + result = append(result, toUpperASCII(byte(c))) + } else { + n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(c)) + result = append(result, runeBuf[:n]...) + } + upper = false + continue + } + if c < utf8.RuneSelf { + result = append(result, byte(c)) + } else { + n := utf8.EncodeRune(runeBuf[:], c) + result = append(result, runeBuf[:n]...) + } + } + candidate := string(result[start:]) + initialism = initialism[:0] + for _, r := range candidate { + if r < utf8.RuneSelf { + initialism = append(initialism, toUpperASCII(byte(r))) + } else { + n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r)) + initialism = append(initialism, runeBuf[:n]...) + } + } + if length := commonInitialism.LookupByBytes(initialism); length > 0 { + result = append(result[:start], initialism...) + } + return string(result) +} + +// ToUpperCamelCaseASCII is similar to ToUpperCamelCase, but optimized for +// only the ASCII characters. +// ToUpperCamelCaseASCII is faster than ToUpperCamelCase, but doesn't work if +// contains non-ASCII characters. +func ToUpperCamelCaseASCII(s string) string { + if s == "" { + return "" + } + upper := true + start := 0 + result := make([]byte, 0, len(s)) + var initialism []byte + for i := 0; i < len(s); i++ { + c := s[i] + if c == '_' { + upper = true + candidate := result[start:] + initialism = initialism[:0] + for _, b := range candidate { + initialism = append(initialism, toUpperASCII(b)) + } + if length := commonInitialism.LookupByBytes(initialism); length > 0 { + result = append(result[:start], initialism...) + } + start = len(result) + continue + } + if upper { + result = append(result, toUpperASCII(c)) + upper = false + continue + } + result = append(result, c) + } + candidate := result[start:] + initialism = initialism[:0] + for _, b := range candidate { + initialism = append(initialism, toUpperASCII(b)) + } + if length := commonInitialism.LookupByBytes(initialism); length > 0 { + result = append(result[:start], initialism...) + } + return string(result) +} + +// ToSnakeCase returns a copy of the string s with all Unicode letters mapped to their snake case. +// It will insert letter of '_' at position of previous letter of uppercase and all +// letters convert to lower case. +// ToSnakeCase does not insert '_' letter into a common initialism word like ID, URL and so on. +func ToSnakeCase(s string) string { + if s == "" { + return "" + } + result := make([]byte, 0, len(s)) + var runeBuf [utf8.UTFMax]byte + var j, skipCount int + for i, c := range s { + if i < skipCount { + continue + } + if unicode.IsUpper(c) { + if i != 0 { + result = append(result, '_') + } + next := nextIndex(j, len(s)) + if length := commonInitialism.Lookup(s[j:next]); length > 0 { + for _, r := range s[j : j+length] { + if r < utf8.RuneSelf { + result = append(result, toLowerASCII(byte(r))) + } else { + n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(r)) + result = append(result, runeBuf[:n]...) + } + } + j += length - 1 + skipCount = i + length + continue + } + } + if c < utf8.RuneSelf { + result = append(result, toLowerASCII(byte(c))) + } else { + n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(c)) + result = append(result, runeBuf[:n]...) + } + j++ + } + return string(result) +} + +// ToSnakeCaseASCII is similar to ToSnakeCase, but optimized for only the ASCII +// characters. +// ToSnakeCaseASCII is faster than ToSnakeCase, but doesn't work correctly if +// contains non-ASCII characters. +func ToSnakeCaseASCII(s string) string { + if s == "" { + return "" + } + result := make([]byte, 0, len(s)) + for i := 0; i < len(s); i++ { + c := s[i] + if isUpperASCII(c) { + if i != 0 { + result = append(result, '_') + } + if k := i + shortestLen - 1; k < len(s) && isUpperASCII(s[k]) { + if length := commonInitialism.Lookup(s[i:nextIndex(i, len(s))]); length > 0 { + for j, buf := 0, s[i:i+length]; j < len(buf); j++ { + result = append(result, toLowerASCII(buf[j])) + } + i += length - 1 + continue + } + } + } + result = append(result, toLowerASCII(c)) + } + return string(result) +} + +// AddCommonInitialism adds ss to list of common initialisms. +func AddCommonInitialism(ss ...string) { + mu.Lock() + defer mu.Unlock() + for _, s := range ss { + commonInitialismMap[s] = struct{}{} + } + commonInitialisms = keys(commonInitialismMap) + commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) + longestLen = longestLength(commonInitialisms) + shortestLen = shortestLength(commonInitialisms, longestLen) +} + +// DelCommonInitialism deletes ss from list of common initialisms. +func DelCommonInitialism(ss ...string) { + mu.Lock() + defer mu.Unlock() + for _, s := range ss { + delete(commonInitialismMap, s) + } + commonInitialisms = keys(commonInitialismMap) + commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) + longestLen = longestLength(commonInitialisms) + shortestLen = shortestLength(commonInitialisms, longestLen) +} + +func isUpperASCII(c byte) bool { + return 'A' <= c && c <= 'Z' +} + +func isLowerASCII(c byte) bool { + return 'a' <= c && c <= 'z' +} + +func toUpperASCII(c byte) byte { + if isLowerASCII(c) { + return c - ('a' - 'A') + } + return c +} + +func toLowerASCII(c byte) byte { + if isUpperASCII(c) { + return c + 'a' - 'A' + } + return c +} + +func nextIndex(i, maxlen int) int { + if n := i + longestLen; n < maxlen { + return n + } + return maxlen +} + +func keys(m map[string]struct{}) []string { + result := make([]string, 0, len(m)) + for k := range m { + result = append(result, k) + } + return result +} + +func shortestLength(strs []string, shortest int) int { + for _, s := range strs { + if candidate := utf8.RuneCountInString(s); candidate < shortest { + shortest = candidate + } + } + return shortest +} + +func longestLength(strs []string) (longest int) { + for _, s := range strs { + if candidate := utf8.RuneCountInString(s); candidate > longest { + longest = candidate + } + } + return longest +} diff --git a/vendor/github.com/naoina/toml/LICENSE b/vendor/github.com/naoina/toml/LICENSE new file mode 100644 index 000000000..e65039ad8 --- /dev/null +++ b/vendor/github.com/naoina/toml/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Naoya Inada <naoina@kuune.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/naoina/toml/README.md b/vendor/github.com/naoina/toml/README.md new file mode 100644 index 000000000..1c0143348 --- /dev/null +++ b/vendor/github.com/naoina/toml/README.md @@ -0,0 +1,392 @@ +# TOML parser and encoder library for Golang [](https://travis-ci.org/naoina/toml) + +[TOML](https://github.com/toml-lang/toml) parser and encoder library for [Golang](http://golang.org/). + +This library is compatible with TOML version [v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md). + +## Installation + + go get -u github.com/naoina/toml + +## Usage + +The following TOML save as `example.toml`. + +```toml +# This is a TOML document. Boom. + +title = "TOML Example" + +[owner] +name = "Lance Uppercut" +dob = 1979-05-27T07:32:00-08:00 # First class dates? Why not? + +[database] +server = "192.168.1.1" +ports = [ 8001, 8001, 8002 ] +connection_max = 5000 +enabled = true + +[servers] + + # You can indent as you please. Tabs or spaces. TOML don't care. + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + +[clients] +data = [ ["gamma", "delta"], [1, 2] ] + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] +``` + +Then above TOML will mapping to `tomlConfig` struct using `toml.Unmarshal`. + +```go +package main + +import ( + "io/ioutil" + "os" + "time" + + "github.com/naoina/toml" +) + +type tomlConfig struct { + Title string + Owner struct { + Name string + Dob time.Time + } + Database struct { + Server string + Ports []int + ConnectionMax uint + Enabled bool + } + Servers map[string]ServerInfo + Clients struct { + Data [][]interface{} + Hosts []string + } +} + +type ServerInfo struct { + IP net.IP + DC string +} + +func main() { + f, err := os.Open("example.toml") + if err != nil { + panic(err) + } + defer f.Close() + var config Config + if err := toml.NewDecoder(f).Decode(&config); err != nil { + panic(err) + } + + // then to use the unmarshaled config... + fmt.Println("IP of server 'alpha':", config.Servers["alpha"].IP) +} +``` + +## Mappings + +A key and value of TOML will map to the corresponding field. +The fields of struct for mapping must be exported. + +The rules of the mapping of key are following: + +#### Exact matching + +```toml +timeout_seconds = 256 +``` + +```go +type Config struct { + Timeout_seconds int +} +``` + +#### Camelcase matching + +```toml +server_name = "srv1" +``` + +```go +type Config struct { + ServerName string +} +``` + +#### Uppercase matching + +```toml +ip = "10.0.0.1" +``` + +```go +type Config struct { + IP string +} +``` + +See the following examples for the value mappings. + +### String + +```toml +val = "string" +``` + +```go +type Config struct { + Val string +} +``` + +### Integer + +```toml +val = 100 +``` + +```go +type Config struct { + Val int +} +``` + +All types that can be used are following: + +* int8 (from `-128` to `127`) +* int16 (from `-32768` to `32767`) +* int32 (from `-2147483648` to `2147483647`) +* int64 (from `-9223372036854775808` to `9223372036854775807`) +* int (same as `int32` on 32bit environment, or `int64` on 64bit environment) +* uint8 (from `0` to `255`) +* uint16 (from `0` to `65535`) +* uint32 (from `0` to `4294967295`) +* uint64 (from `0` to `18446744073709551615`) +* uint (same as `uint` on 32bit environment, or `uint64` on 64bit environment) + +### Float + +```toml +val = 3.1415 +``` + +```go +type Config struct { + Val float32 +} +``` + +All types that can be used are following: + +* float32 +* float64 + +### Boolean + +```toml +val = true +``` + +```go +type Config struct { + Val bool +} +``` + +### Datetime + +```toml +val = 2014-09-28T21:27:39Z +``` + +```go +type Config struct { + Val time.Time +} +``` + +### Array + +```toml +val = ["a", "b", "c"] +``` + +```go +type Config struct { + Val []string +} +``` + +Also following examples all can be mapped: + +```toml +val1 = [1, 2, 3] +val2 = [["a", "b"], ["c", "d"]] +val3 = [[1, 2, 3], ["a", "b", "c"]] +val4 = [[1, 2, 3], [["a", "b"], [true, false]]] +``` + +```go +type Config struct { + Val1 []int + Val2 [][]string + Val3 [][]interface{} + Val4 [][]interface{} +} +``` + +### Table + +```toml +[server] +type = "app" + + [server.development] + ip = "10.0.0.1" + + [server.production] + ip = "10.0.0.2" +``` + +```go +type Config struct { + Server map[string]Server +} + +type Server struct { + IP string +} +``` + +You can also use the following struct instead of map of struct. + +```go +type Config struct { + Server struct { + Development Server + Production Server + } +} + +type Server struct { + IP string +} +``` + +### Array of Tables + +```toml +[[fruit]] + name = "apple" + + [fruit.physical] + color = "red" + shape = "round" + + [[fruit.variety]] + name = "red delicious" + + [[fruit.variety]] + name = "granny smith" + +[[fruit]] + name = "banana" + + [[fruit.variety]] + name = "plantain" +``` + +```go +type Config struct { + Fruit []struct { + Name string + Physical struct { + Color string + Shape string + } + Variety []struct { + Name string + } + } +} +``` + +### Using the `encoding.TextUnmarshaler` interface + +Package toml supports `encoding.TextUnmarshaler` (and `encoding.TextMarshaler`). You can +use it to apply custom marshaling rules for certain types. The `UnmarshalText` method is +called with the value text found in the TOML input. TOML strings are passed unquoted. + +```toml +duration = "10s" +``` + +```go +import time + +type Duration time.Duration + +// UnmarshalText implements encoding.TextUnmarshaler +func (d *Duration) UnmarshalText(data []byte) error { + duration, err := time.ParseDuration(string(data)) + if err == nil { + *d = Duration(duration) + } + return err +} + +// MarshalText implements encoding.TextMarshaler +func (d Duration) MarshalText() ([]byte, error) { + return []byte(time.Duration(d).String()), nil +} + +type ConfigWithDuration struct { + Duration Duration +} +``` +### Using the `toml.UnmarshalerRec` interface + +You can also override marshaling rules specifically for TOML using the `UnmarshalerRec` +and `MarshalerRec` interfaces. These are useful if you want to control how structs or +arrays are handled. You can apply additional validation or set unexported struct fields. + +Note: `encoding.TextUnmarshaler` and `encoding.TextMarshaler` should be preferred for +simple (scalar) values because they're also compatible with other formats like JSON or +YAML. + +[See the UnmarshalerRec example](https://godoc.org/github.com/naoina/toml/#example_UnmarshalerRec). + +### Using the `toml.Unmarshaler` interface + +If you want to deal with raw TOML syntax, use the `Unmarshaler` and `Marshaler` +interfaces. Their input and output is raw TOML syntax. As such, these interfaces are +useful if you want to handle TOML at the syntax level. + +[See the Unmarshaler example](https://godoc.org/github.com/naoina/toml/#example_Unmarshaler). + +## API documentation + +See [Godoc](http://godoc.org/github.com/naoina/toml). + +## License + +MIT diff --git a/vendor/github.com/naoina/toml/ast/ast.go b/vendor/github.com/naoina/toml/ast/ast.go new file mode 100644 index 000000000..4868e2e1a --- /dev/null +++ b/vendor/github.com/naoina/toml/ast/ast.go @@ -0,0 +1,192 @@ +package ast + +import ( + "strconv" + "strings" + "time" +) + +type Position struct { + Begin int + End int +} + +type Value interface { + Pos() int + End() int + Source() string +} + +type String struct { + Position Position + Value string + Data []rune +} + +func (s *String) Pos() int { + return s.Position.Begin +} + +func (s *String) End() int { + return s.Position.End +} + +func (s *String) Source() string { + return string(s.Data) +} + +type Integer struct { + Position Position + Value string + Data []rune +} + +func (i *Integer) Pos() int { + return i.Position.Begin +} + +func (i *Integer) End() int { + return i.Position.End +} + +func (i *Integer) Source() string { + return string(i.Data) +} + +func (i *Integer) Int() (int64, error) { + return strconv.ParseInt(i.Value, 10, 64) +} + +type Float struct { + Position Position + Value string + Data []rune +} + +func (f *Float) Pos() int { + return f.Position.Begin +} + +func (f *Float) End() int { + return f.Position.End +} + +func (f *Float) Source() string { + return string(f.Data) +} + +func (f *Float) Float() (float64, error) { + return strconv.ParseFloat(f.Value, 64) +} + +type Boolean struct { + Position Position + Value string + Data []rune +} + +func (b *Boolean) Pos() int { + return b.Position.Begin +} + +func (b *Boolean) End() int { + return b.Position.End +} + +func (b *Boolean) Source() string { + return string(b.Data) +} + +func (b *Boolean) Boolean() (bool, error) { + return strconv.ParseBool(b.Value) +} + +type Datetime struct { + Position Position + Value string + Data []rune +} + +func (d *Datetime) Pos() int { + return d.Position.Begin +} + +func (d *Datetime) End() int { + return d.Position.End +} + +func (d *Datetime) Source() string { + return string(d.Data) +} + +func (d *Datetime) Time() (time.Time, error) { + switch { + case !strings.Contains(d.Value, ":"): + return time.Parse("2006-01-02", d.Value) + case !strings.Contains(d.Value, "-"): + return time.Parse("15:04:05.999999999", d.Value) + default: + return time.Parse(time.RFC3339Nano, d.Value) + } +} + +type Array struct { + Position Position + Value []Value + Data []rune +} + +func (a *Array) Pos() int { + return a.Position.Begin +} + +func (a *Array) End() int { + return a.Position.End +} + +func (a *Array) Source() string { + return string(a.Data) +} + +type TableType uint8 + +const ( + TableTypeNormal TableType = iota + TableTypeArray +) + +var tableTypes = [...]string{ + "normal", + "array", +} + +func (t TableType) String() string { + return tableTypes[t] +} + +type Table struct { + Position Position + Line int + Name string + Fields map[string]interface{} + Type TableType + Data []rune +} + +func (t *Table) Pos() int { + return t.Position.Begin +} + +func (t *Table) End() int { + return t.Position.End +} + +func (t *Table) Source() string { + return string(t.Data) +} + +type KeyValue struct { + Key string + Value Value + Line int +} diff --git a/vendor/github.com/naoina/toml/config.go b/vendor/github.com/naoina/toml/config.go new file mode 100644 index 000000000..06bb9493b --- /dev/null +++ b/vendor/github.com/naoina/toml/config.go @@ -0,0 +1,86 @@ +package toml + +import ( + "fmt" + "io" + "reflect" + "strings" + + stringutil "github.com/naoina/go-stringutil" + "github.com/naoina/toml/ast" +) + +// Config contains options for encoding and decoding. +type Config struct { + // NormFieldName is used to match TOML keys to struct fields. The function runs for + // both input keys and struct field names and should return a string that makes the + // two match. You must set this field to use the decoder. + // + // Example: The function in the default config removes _ and lowercases all keys. This + // allows a key called 'api_key' to match the struct field 'APIKey' because both are + // normalized to 'apikey'. + // + // Note that NormFieldName is not used for fields which define a TOML + // key through the struct tag. + NormFieldName func(typ reflect.Type, keyOrField string) string + + // FieldToKey determines the TOML key of a struct field when encoding. + // You must set this field to use the encoder. + // + // Note that FieldToKey is not used for fields which define a TOML + // key through the struct tag. + FieldToKey func(typ reflect.Type, field string) string + + // MissingField, if non-nil, is called when the decoder encounters a key for which no + // matching struct field exists. The default behavior is to return an error. + MissingField func(typ reflect.Type, key string) error +} + +// DefaultConfig contains the default options for encoding and decoding. +// Snake case (i.e. 'foo_bar') is used for key names. +var DefaultConfig = Config{ + NormFieldName: defaultNormFieldName, + FieldToKey: snakeCase, +} + +func defaultNormFieldName(typ reflect.Type, s string) string { + return strings.Replace(strings.ToLower(s), "_", "", -1) +} + +func snakeCase(typ reflect.Type, s string) string { + return stringutil.ToSnakeCase(s) +} + +func defaultMissingField(typ reflect.Type, key string) error { + return fmt.Errorf("field corresponding to `%s' is not defined in %v", key, typ) +} + +// NewEncoder returns a new Encoder that writes to w. +// It is shorthand for DefaultConfig.NewEncoder(w). +func NewEncoder(w io.Writer) *Encoder { + return DefaultConfig.NewEncoder(w) +} + +// Marshal returns the TOML encoding of v. +// It is shorthand for DefaultConfig.Marshal(v). +func Marshal(v interface{}) ([]byte, error) { + return DefaultConfig.Marshal(v) +} + +// Unmarshal parses the TOML data and stores the result in the value pointed to by v. +// It is shorthand for DefaultConfig.Unmarshal(data, v). +func Unmarshal(data []byte, v interface{}) error { + return DefaultConfig.Unmarshal(data, v) +} + +// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v. +// It is shorthand for DefaultConfig.UnmarshalTable(t, v). +func UnmarshalTable(t *ast.Table, v interface{}) error { + return DefaultConfig.UnmarshalTable(t, v) +} + +// NewDecoder returns a new Decoder that reads from r. +// It is shorthand for DefaultConfig.NewDecoder(r). +func NewDecoder(r io.Reader) *Decoder { + return DefaultConfig.NewDecoder(r) +} diff --git a/vendor/github.com/naoina/toml/decode.go b/vendor/github.com/naoina/toml/decode.go new file mode 100644 index 000000000..b3c169eb1 --- /dev/null +++ b/vendor/github.com/naoina/toml/decode.go @@ -0,0 +1,478 @@ +// Package toml encodes and decodes the TOML configuration format using reflection. +// +// This library is compatible with TOML version v0.4.0. +package toml + +import ( + "encoding" + "fmt" + "io" + "io/ioutil" + "reflect" + "strconv" + "strings" + "time" + + "github.com/naoina/toml/ast" +) + +const ( + tableSeparator = '.' +) + +var ( + escapeReplacer = strings.NewReplacer( + "\b", "\\n", + "\f", "\\f", + "\n", "\\n", + "\r", "\\r", + "\t", "\\t", + ) + underscoreReplacer = strings.NewReplacer( + "_", "", + ) +) + +var timeType = reflect.TypeOf(time.Time{}) + +// Unmarshal parses the TOML data and stores the result in the value pointed to by v. +// +// Unmarshal will mapped to v that according to following rules: +// +// TOML strings to string +// TOML integers to any int type +// TOML floats to float32 or float64 +// TOML booleans to bool +// TOML datetimes to time.Time +// TOML arrays to any type of slice +// TOML tables to struct or map +// TOML array tables to slice of struct or map +func (cfg *Config) Unmarshal(data []byte, v interface{}) error { + table, err := Parse(data) + if err != nil { + return err + } + if err := cfg.UnmarshalTable(table, v); err != nil { + return err + } + return nil +} + +// A Decoder reads and decodes TOML from an input stream. +type Decoder struct { + r io.Reader + cfg *Config +} + +// NewDecoder returns a new Decoder that reads from r. +// Note that it reads all from r before parsing it. +func (cfg *Config) NewDecoder(r io.Reader) *Decoder { + return &Decoder{r, cfg} +} + +// Decode parses the TOML data from its input and stores it in the value pointed to by v. +// See the documentation for Unmarshal for details about the conversion of TOML into a Go value. +func (d *Decoder) Decode(v interface{}) error { + b, err := ioutil.ReadAll(d.r) + if err != nil { + return err + } + return d.cfg.Unmarshal(b, v) +} + +// UnmarshalerRec may be implemented by types to customize their behavior when being +// unmarshaled from TOML. You can use it to implement custom validation or to set +// unexported fields. +// +// UnmarshalTOML receives a function that can be called to unmarshal the original TOML +// value into a field or variable. It is safe to call the function more than once if +// necessary. +type UnmarshalerRec interface { + UnmarshalTOML(fn func(interface{}) error) error +} + +// Unmarshaler can be used to capture and process raw TOML source of a table or value. +// UnmarshalTOML must copy the input if it wishes to retain it after returning. +// +// Note: this interface is retained for backwards compatibility. You probably want +// to implement encoding.TextUnmarshaler or UnmarshalerRec instead. +type Unmarshaler interface { + UnmarshalTOML(input []byte) error +} + +// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v. +// +// UnmarshalTable will mapped to v that according to following rules: +// +// TOML strings to string +// TOML integers to any int type +// TOML floats to float32 or float64 +// TOML booleans to bool +// TOML datetimes to time.Time +// TOML arrays to any type of slice +// TOML tables to struct or map +// TOML array tables to slice of struct or map +func (cfg *Config) UnmarshalTable(t *ast.Table, v interface{}) error { + rv := reflect.ValueOf(v) + toplevelMap := rv.Kind() == reflect.Map + if (!toplevelMap && rv.Kind() != reflect.Ptr) || rv.IsNil() { + return &invalidUnmarshalError{reflect.TypeOf(v)} + } + return unmarshalTable(cfg, rv, t, toplevelMap) +} + +// used for UnmarshalerRec. +func unmarshalTableOrValue(cfg *Config, rv reflect.Value, av interface{}) error { + if (rv.Kind() != reflect.Ptr && rv.Kind() != reflect.Map) || rv.IsNil() { + return &invalidUnmarshalError{rv.Type()} + } + rv = indirect(rv) + + switch av.(type) { + case *ast.KeyValue, *ast.Table, []*ast.Table: + if err := unmarshalField(cfg, rv, av); err != nil { + return lineError(fieldLineNumber(av), err) + } + return nil + case ast.Value: + return setValue(cfg, rv, av.(ast.Value)) + default: + panic(fmt.Sprintf("BUG: unhandled AST node type %T", av)) + } +} + +// unmarshalTable unmarshals the fields of a table into a struct or map. +// +// toplevelMap is true when rv is an (unadressable) map given to UnmarshalTable. In this +// (special) case, the map is used as-is instead of creating a new map. +func unmarshalTable(cfg *Config, rv reflect.Value, t *ast.Table, toplevelMap bool) error { + rv = indirect(rv) + if err, ok := setUnmarshaler(cfg, rv, t); ok { + return lineError(t.Line, err) + } + switch { + case rv.Kind() == reflect.Struct: + fc := makeFieldCache(cfg, rv.Type()) + for key, fieldAst := range t.Fields { + fv, fieldName, err := fc.findField(cfg, rv, key) + if err != nil { + return lineError(fieldLineNumber(fieldAst), err) + } + if fv.IsValid() { + if err := unmarshalField(cfg, fv, fieldAst); err != nil { + return lineErrorField(fieldLineNumber(fieldAst), rv.Type().String()+"."+fieldName, err) + } + } + } + case rv.Kind() == reflect.Map || isEface(rv): + m := rv + if !toplevelMap { + if rv.Kind() == reflect.Interface { + m = reflect.ValueOf(make(map[string]interface{})) + } else { + m = reflect.MakeMap(rv.Type()) + } + } + elemtyp := m.Type().Elem() + for key, fieldAst := range t.Fields { + kv, err := unmarshalMapKey(m.Type().Key(), key) + if err != nil { + return lineError(fieldLineNumber(fieldAst), err) + } + fv := reflect.New(elemtyp).Elem() + if err := unmarshalField(cfg, fv, fieldAst); err != nil { + return lineError(fieldLineNumber(fieldAst), err) + } + m.SetMapIndex(kv, fv) + } + if !toplevelMap { + rv.Set(m) + } + default: + return lineError(t.Line, &unmarshalTypeError{"table", "struct or map", rv.Type()}) + } + return nil +} + +func fieldLineNumber(fieldAst interface{}) int { + switch av := fieldAst.(type) { + case *ast.KeyValue: + return av.Line + case *ast.Table: + return av.Line + case []*ast.Table: + return av[0].Line + default: + panic(fmt.Sprintf("BUG: unhandled node type %T", fieldAst)) + } +} + +func unmarshalField(cfg *Config, rv reflect.Value, fieldAst interface{}) error { + switch av := fieldAst.(type) { + case *ast.KeyValue: + return setValue(cfg, rv, av.Value) + case *ast.Table: + return unmarshalTable(cfg, rv, av, false) + case []*ast.Table: + rv = indirect(rv) + if err, ok := setUnmarshaler(cfg, rv, fieldAst); ok { + return err + } + var slice reflect.Value + switch { + case rv.Kind() == reflect.Slice: + slice = reflect.MakeSlice(rv.Type(), len(av), len(av)) + case isEface(rv): + slice = reflect.ValueOf(make([]interface{}, len(av))) + default: + return &unmarshalTypeError{"array table", "slice", rv.Type()} + } + for i, tbl := range av { + vv := reflect.New(slice.Type().Elem()).Elem() + if err := unmarshalTable(cfg, vv, tbl, false); err != nil { + return err + } + slice.Index(i).Set(vv) + } + rv.Set(slice) + default: + panic(fmt.Sprintf("BUG: unhandled AST node type %T", av)) + } + return nil +} + +func unmarshalMapKey(typ reflect.Type, key string) (reflect.Value, error) { + rv := reflect.New(typ).Elem() + if u, ok := rv.Addr().Interface().(encoding.TextUnmarshaler); ok { + return rv, u.UnmarshalText([]byte(key)) + } + switch typ.Kind() { + case reflect.String: + rv.SetString(key) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i, err := strconv.ParseInt(key, 10, int(typ.Size()*8)) + if err != nil { + return rv, convertNumError(typ.Kind(), err) + } + rv.SetInt(i) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + i, err := strconv.ParseUint(key, 10, int(typ.Size()*8)) + if err != nil { + return rv, convertNumError(typ.Kind(), err) + } + rv.SetUint(i) + default: + return rv, fmt.Errorf("invalid map key type %s", typ) + } + return rv, nil +} + +func setValue(cfg *Config, lhs reflect.Value, val ast.Value) error { + lhs = indirect(lhs) + if err, ok := setUnmarshaler(cfg, lhs, val); ok { + return err + } + if err, ok := setTextUnmarshaler(lhs, val); ok { + return err + } + switch v := val.(type) { + case *ast.Integer: + return setInt(lhs, v) + case *ast.Float: + return setFloat(lhs, v) + case *ast.String: + return setString(lhs, v) + case *ast.Boolean: + return setBoolean(lhs, v) + case *ast.Datetime: + return setDatetime(lhs, v) + case *ast.Array: + return setArray(cfg, lhs, v) + default: + panic(fmt.Sprintf("BUG: unhandled node type %T", v)) + } +} + +func indirect(rv reflect.Value) reflect.Value { + for rv.Kind() == reflect.Ptr { + if rv.IsNil() { + rv.Set(reflect.New(rv.Type().Elem())) + } + rv = rv.Elem() + } + return rv +} + +func setUnmarshaler(cfg *Config, lhs reflect.Value, av interface{}) (error, bool) { + if lhs.CanAddr() { + if u, ok := lhs.Addr().Interface().(UnmarshalerRec); ok { + err := u.UnmarshalTOML(func(v interface{}) error { + return unmarshalTableOrValue(cfg, reflect.ValueOf(v), av) + }) + return err, true + } + if u, ok := lhs.Addr().Interface().(Unmarshaler); ok { + return u.UnmarshalTOML(unmarshalerSource(av)), true + } + } + return nil, false +} + +func unmarshalerSource(av interface{}) []byte { + var source []byte + switch av := av.(type) { + case []*ast.Table: + for i, tab := range av { + source = append(source, tab.Source()...) + if i != len(av)-1 { + source = append(source, '\n') + } + } + case ast.Value: + source = []byte(av.Source()) + default: + panic(fmt.Sprintf("BUG: unhandled node type %T", av)) + } + return source +} + +func setTextUnmarshaler(lhs reflect.Value, val ast.Value) (error, bool) { + if !lhs.CanAddr() { + return nil, false + } + u, ok := lhs.Addr().Interface().(encoding.TextUnmarshaler) + if !ok || lhs.Type() == timeType { + return nil, false + } + var data string + switch val := val.(type) { + case *ast.Array: + return &unmarshalTypeError{"array", "", lhs.Type()}, true + case *ast.String: + data = val.Value + default: + data = val.Source() + } + return u.UnmarshalText([]byte(data)), true +} + +func setInt(fv reflect.Value, v *ast.Integer) error { + k := fv.Kind() + switch { + case k >= reflect.Int && k <= reflect.Int64: + i, err := strconv.ParseInt(v.Value, 10, int(fv.Type().Size()*8)) + if err != nil { + return convertNumError(fv.Kind(), err) + } + fv.SetInt(i) + case k >= reflect.Uint && k <= reflect.Uintptr: + i, err := strconv.ParseUint(v.Value, 10, int(fv.Type().Size()*8)) + if err != nil { + return convertNumError(fv.Kind(), err) + } + fv.SetUint(i) + case isEface(fv): + i, err := strconv.ParseInt(v.Value, 10, 64) + if err != nil { + return convertNumError(reflect.Int64, err) + } + fv.Set(reflect.ValueOf(i)) + default: + return &unmarshalTypeError{"integer", "", fv.Type()} + } + return nil +} + +func setFloat(fv reflect.Value, v *ast.Float) error { + f, err := v.Float() + if err != nil { + return err + } + switch { + case fv.Kind() == reflect.Float32 || fv.Kind() == reflect.Float64: + if fv.OverflowFloat(f) { + return &overflowError{fv.Kind(), v.Value} + } + fv.SetFloat(f) + case isEface(fv): + fv.Set(reflect.ValueOf(f)) + default: + return &unmarshalTypeError{"float", "", fv.Type()} + } + return nil +} + +func setString(fv reflect.Value, v *ast.String) error { + switch { + case fv.Kind() == reflect.String: + fv.SetString(v.Value) + case isEface(fv): + fv.Set(reflect.ValueOf(v.Value)) + default: + return &unmarshalTypeError{"string", "", fv.Type()} + } + return nil +} + +func setBoolean(fv reflect.Value, v *ast.Boolean) error { + b, _ := v.Boolean() + switch { + case fv.Kind() == reflect.Bool: + fv.SetBool(b) + case isEface(fv): + fv.Set(reflect.ValueOf(b)) + default: + return &unmarshalTypeError{"boolean", "", fv.Type()} + } + return nil +} + +func setDatetime(rv reflect.Value, v *ast.Datetime) error { + t, err := v.Time() + if err != nil { + return err + } + if !timeType.AssignableTo(rv.Type()) { + return &unmarshalTypeError{"datetime", "", rv.Type()} + } + rv.Set(reflect.ValueOf(t)) + return nil +} + +func setArray(cfg *Config, rv reflect.Value, v *ast.Array) error { + var slicetyp reflect.Type + switch { + case rv.Kind() == reflect.Slice: + slicetyp = rv.Type() + case isEface(rv): + slicetyp = reflect.SliceOf(rv.Type()) + default: + return &unmarshalTypeError{"array", "slice", rv.Type()} + } + + if len(v.Value) == 0 { + // Ensure defined slices are always set to a non-nil value. + rv.Set(reflect.MakeSlice(slicetyp, 0, 0)) + return nil + } + + tomltyp := reflect.TypeOf(v.Value[0]) + slice := reflect.MakeSlice(slicetyp, len(v.Value), len(v.Value)) + typ := slicetyp.Elem() + for i, vv := range v.Value { + if i > 0 && tomltyp != reflect.TypeOf(vv) { + return errArrayMultiType + } + tmp := reflect.New(typ).Elem() + if err := setValue(cfg, tmp, vv); err != nil { + return err + } + slice.Index(i).Set(tmp) + } + rv.Set(slice) + return nil +} + +func isEface(rv reflect.Value) bool { + return rv.Kind() == reflect.Interface && rv.Type().NumMethod() == 0 +} diff --git a/vendor/github.com/naoina/toml/encode.go b/vendor/github.com/naoina/toml/encode.go new file mode 100644 index 000000000..ae6bfd575 --- /dev/null +++ b/vendor/github.com/naoina/toml/encode.go @@ -0,0 +1,398 @@ +package toml + +import ( + "bytes" + "encoding" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "time" + + "github.com/naoina/toml/ast" +) + +const ( + tagOmitempty = "omitempty" + tagSkip = "-" +) + +// Marshal returns the TOML encoding of v. +// +// Struct values encode as TOML. Each exported struct field becomes a field of +// the TOML structure unless +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option. +// +// The "toml" key in the struct field's tag value is the key name, followed by +// an optional comma and options. Examples: +// +// // Field is ignored by this package. +// Field int `toml:"-"` +// +// // Field appears in TOML as key "myName". +// Field int `toml:"myName"` +// +// // Field appears in TOML as key "myName" and the field is omitted from the +// // result of encoding if its value is empty. +// Field int `toml:"myName,omitempty"` +// +// // Field appears in TOML as key "field", but the field is skipped if +// // empty. Note the leading comma. +// Field int `toml:",omitempty"` +func (cfg *Config) Marshal(v interface{}) ([]byte, error) { + buf := new(bytes.Buffer) + err := cfg.NewEncoder(buf).Encode(v) + return buf.Bytes(), err +} + +// A Encoder writes TOML to an output stream. +type Encoder struct { + w io.Writer + cfg *Config +} + +// NewEncoder returns a new Encoder that writes to w. +func (cfg *Config) NewEncoder(w io.Writer) *Encoder { + return &Encoder{w, cfg} +} + +// Encode writes the TOML of v to the stream. +// See the documentation for Marshal for details about the conversion of Go values to TOML. +func (e *Encoder) Encode(v interface{}) error { + rv := reflect.ValueOf(v) + for rv.Kind() == reflect.Ptr { + if rv.IsNil() { + return &marshalNilError{rv.Type()} + } + rv = rv.Elem() + } + buf := &tableBuf{typ: ast.TableTypeNormal} + var err error + switch rv.Kind() { + case reflect.Struct: + err = buf.structFields(e.cfg, rv) + case reflect.Map: + err = buf.mapFields(e.cfg, rv) + default: + err = &marshalTableError{rv.Type()} + } + if err != nil { + return err + } + return buf.writeTo(e.w, "") +} + +// Marshaler can be implemented to override the encoding of TOML values. The returned text +// must be a simple TOML value (i.e. not a table) and is inserted into marshaler output. +// +// This interface exists for backwards-compatibility reasons. You probably want to +// implement encoding.TextMarshaler or MarshalerRec instead. +type Marshaler interface { + MarshalTOML() ([]byte, error) +} + +// MarshalerRec can be implemented to override the TOML encoding of a type. +// The returned value is marshaled in place of the receiver. +type MarshalerRec interface { + MarshalTOML() (interface{}, error) +} + +type tableBuf struct { + name string // already escaped / quoted + body []byte + children []*tableBuf + typ ast.TableType + arrayDepth int +} + +func (b *tableBuf) writeTo(w io.Writer, prefix string) error { + key := b.name // TODO: escape dots + if prefix != "" { + key = prefix + "." + key + } + + if b.name != "" { + head := "[" + key + "]" + if b.typ == ast.TableTypeArray { + head = "[" + head + "]" + } + head += "\n" + if _, err := io.WriteString(w, head); err != nil { + return err + } + } + if _, err := w.Write(b.body); err != nil { + return err + } + + for i, child := range b.children { + if len(b.body) > 0 || i > 0 { + if _, err := w.Write([]byte("\n")); err != nil { + return err + } + } + if err := child.writeTo(w, key); err != nil { + return err + } + } + return nil +} + +func (b *tableBuf) newChild(name string) *tableBuf { + child := &tableBuf{name: quoteName(name), typ: ast.TableTypeNormal} + if b.arrayDepth > 0 { + child.typ = ast.TableTypeArray + } + return child +} + +func (b *tableBuf) addChild(child *tableBuf) { + // Empty table elision: we can avoid writing a table that doesn't have any keys on its + // own. Array tables can't be elided because they define array elements (which would + // be missing if elided). + if len(child.body) == 0 && child.typ == ast.TableTypeNormal { + for _, gchild := range child.children { + gchild.name = child.name + "." + gchild.name + b.addChild(gchild) + } + return + } + b.children = append(b.children, child) +} + +func (b *tableBuf) structFields(cfg *Config, rv reflect.Value) error { + rt := rv.Type() + for i := 0; i < rv.NumField(); i++ { + ft := rt.Field(i) + if ft.PkgPath != "" && !ft.Anonymous { // not exported + continue + } + name, rest := extractTag(ft.Tag.Get(fieldTagName)) + if name == tagSkip { + continue + } + fv := rv.Field(i) + if rest == tagOmitempty && isEmptyValue(fv) { + continue + } + if name == "" { + name = cfg.FieldToKey(rt, ft.Name) + } + if err := b.field(cfg, name, fv); err != nil { + return err + } + } + return nil +} + +type mapKeyList []struct { + key string + value reflect.Value +} + +func (l mapKeyList) Len() int { return len(l) } +func (l mapKeyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l mapKeyList) Less(i, j int) bool { return l[i].key < l[j].key } + +func (b *tableBuf) mapFields(cfg *Config, rv reflect.Value) error { + keys := rv.MapKeys() + keylist := make(mapKeyList, len(keys)) + for i, key := range keys { + var err error + keylist[i].key, err = encodeMapKey(key) + if err != nil { + return err + } + keylist[i].value = rv.MapIndex(key) + } + sort.Sort(keylist) + + for _, kv := range keylist { + if err := b.field(cfg, kv.key, kv.value); err != nil { + return err + } + } + return nil +} + +func (b *tableBuf) field(cfg *Config, name string, rv reflect.Value) error { + off := len(b.body) + b.body = append(b.body, quoteName(name)...) + b.body = append(b.body, " = "...) + isTable, err := b.value(cfg, rv, name) + if isTable { + b.body = b.body[:off] // rub out "key =" + } else { + b.body = append(b.body, '\n') + } + return err +} + +func (b *tableBuf) value(cfg *Config, rv reflect.Value, name string) (bool, error) { + isMarshaler, isTable, err := b.marshaler(cfg, rv, name) + if isMarshaler { + return isTable, err + } + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + b.body = strconv.AppendInt(b.body, rv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + b.body = strconv.AppendUint(b.body, rv.Uint(), 10) + case reflect.Float32, reflect.Float64: + b.body = strconv.AppendFloat(b.body, rv.Float(), 'e', -1, 64) + case reflect.Bool: + b.body = strconv.AppendBool(b.body, rv.Bool()) + case reflect.String: + b.body = strconv.AppendQuote(b.body, rv.String()) + case reflect.Ptr, reflect.Interface: + if rv.IsNil() { + return false, &marshalNilError{rv.Type()} + } + return b.value(cfg, rv.Elem(), name) + case reflect.Slice, reflect.Array: + rvlen := rv.Len() + if rvlen == 0 { + b.body = append(b.body, '[', ']') + return false, nil + } + + b.arrayDepth++ + wroteElem := false + b.body = append(b.body, '[') + for i := 0; i < rvlen; i++ { + isTable, err := b.value(cfg, rv.Index(i), name) + if err != nil { + return isTable, err + } + wroteElem = wroteElem || !isTable + if wroteElem { + if i < rvlen-1 { + b.body = append(b.body, ',', ' ') + } else { + b.body = append(b.body, ']') + } + } + } + if !wroteElem { + b.body = b.body[:len(b.body)-1] // rub out '[' + } + b.arrayDepth-- + return !wroteElem, nil + case reflect.Struct: + child := b.newChild(name) + err := child.structFields(cfg, rv) + b.addChild(child) + return true, err + case reflect.Map: + child := b.newChild(name) + err := child.mapFields(cfg, rv) + b.addChild(child) + return true, err + default: + return false, fmt.Errorf("toml: marshal: unsupported type %v", rv.Kind()) + } + return false, nil +} + +func (b *tableBuf) marshaler(cfg *Config, rv reflect.Value, name string) (handled, isTable bool, err error) { + switch t := rv.Interface().(type) { + case encoding.TextMarshaler: + enc, err := t.MarshalText() + if err != nil { + return true, false, err + } + b.body = encodeTextMarshaler(b.body, string(enc)) + return true, false, nil + case MarshalerRec: + newval, err := t.MarshalTOML() + if err != nil { + return true, false, err + } + isTable, err = b.value(cfg, reflect.ValueOf(newval), name) + return true, isTable, err + case Marshaler: + enc, err := t.MarshalTOML() + if err != nil { + return true, false, err + } + b.body = append(b.body, enc...) + return true, false, nil + } + return false, false, nil +} + +func encodeTextMarshaler(buf []byte, v string) []byte { + // Emit the value without quotes if possible. + if v == "true" || v == "false" { + return append(buf, v...) + } else if _, err := time.Parse(time.RFC3339Nano, v); err == nil { + return append(buf, v...) + } else if _, err := strconv.ParseInt(v, 10, 64); err == nil { + return append(buf, v...) + } else if _, err := strconv.ParseUint(v, 10, 64); err == nil { + return append(buf, v...) + } else if _, err := strconv.ParseFloat(v, 64); err == nil { + return append(buf, v...) + } + return strconv.AppendQuote(buf, v) +} + +func encodeMapKey(rv reflect.Value) (string, error) { + if rv.Kind() == reflect.String { + return rv.String(), nil + } + if tm, ok := rv.Interface().(encoding.TextMarshaler); ok { + b, err := tm.MarshalText() + return string(b), err + } + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(rv.Int(), 10), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return strconv.FormatUint(rv.Uint(), 10), nil + } + return "", fmt.Errorf("toml: invalid map key type %v", rv.Type()) +} + +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array: + // encoding/json treats all arrays with non-zero length as non-empty. We check the + // array content here because zero-length arrays are almost never used. + len := v.Len() + for i := 0; i < len; i++ { + if !isEmptyValue(v.Index(i)) { + return false + } + } + return true + case reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + +func quoteName(s string) string { + if len(s) == 0 { + return strconv.Quote(s) + } + for _, r := range s { + if r >= '0' && r <= '9' || r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r == '-' || r == '_' { + continue + } + return strconv.Quote(s) + } + return s +} diff --git a/vendor/github.com/naoina/toml/error.go b/vendor/github.com/naoina/toml/error.go new file mode 100644 index 000000000..cb73b5e0a --- /dev/null +++ b/vendor/github.com/naoina/toml/error.go @@ -0,0 +1,107 @@ +package toml + +import ( + "errors" + "fmt" + "reflect" + "strconv" +) + +var ( + errArrayMultiType = errors.New("array can't contain multiple types") +) + +// LineError is returned by Unmarshal, UnmarshalTable and Parse +// if the error is local to a line. +type LineError struct { + Line int + StructField string + Err error +} + +func (err *LineError) Error() string { + field := "" + if err.StructField != "" { + field = "(" + err.StructField + ") " + } + return fmt.Sprintf("line %d: %s%v", err.Line, field, err.Err) +} + +func lineError(line int, err error) error { + if err == nil { + return nil + } + if _, ok := err.(*LineError); ok { + return err + } + return &LineError{Line: line, Err: err} +} + +func lineErrorField(line int, field string, err error) error { + if lerr, ok := err.(*LineError); ok { + return lerr + } else if err != nil { + err = &LineError{Line: line, StructField: field, Err: err} + } + return err +} + +type overflowError struct { + kind reflect.Kind + v string +} + +func (err *overflowError) Error() string { + return fmt.Sprintf("value %s is out of range for %v", err.v, err.kind) +} + +func convertNumError(kind reflect.Kind, err error) error { + if numerr, ok := err.(*strconv.NumError); ok && numerr.Err == strconv.ErrRange { + return &overflowError{kind, numerr.Num} + } + return err +} + +type invalidUnmarshalError struct { + typ reflect.Type +} + +func (err *invalidUnmarshalError) Error() string { + if err.typ == nil { + return "toml: Unmarshal(nil)" + } + if err.typ.Kind() != reflect.Ptr { + return "toml: Unmarshal(non-pointer " + err.typ.String() + ")" + } + return "toml: Unmarshal(nil " + err.typ.String() + ")" +} + +type unmarshalTypeError struct { + what string + want string + typ reflect.Type +} + +func (err *unmarshalTypeError) Error() string { + msg := fmt.Sprintf("cannot unmarshal TOML %s into %s", err.what, err.typ) + if err.want != "" { + msg += " (need " + err.want + ")" + } + return msg +} + +type marshalNilError struct { + typ reflect.Type +} + +func (err *marshalNilError) Error() string { + return fmt.Sprintf("toml: cannot marshal nil %s", err.typ) +} + +type marshalTableError struct { + typ reflect.Type +} + +func (err *marshalTableError) Error() string { + return fmt.Sprintf("toml: cannot marshal %s as table, want struct or map type", err.typ) +} diff --git a/vendor/github.com/naoina/toml/parse.go b/vendor/github.com/naoina/toml/parse.go new file mode 100644 index 000000000..e6f95001e --- /dev/null +++ b/vendor/github.com/naoina/toml/parse.go @@ -0,0 +1,376 @@ +package toml + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/naoina/toml/ast" +) + +// The parser is generated by github.com/pointlander/peg. To regenerate it, do: +// +// go get -u github.com/pointlander/peg +// go generate . + +//go:generate peg -switch -inline parse.peg + +var errParse = errors.New("invalid TOML syntax") + +// Parse returns an AST representation of TOML. +// The toplevel is represented by a table. +func Parse(data []byte) (*ast.Table, error) { + d := &parseState{p: &tomlParser{Buffer: string(data)}} + d.init() + + if err := d.parse(); err != nil { + return nil, err + } + + return d.p.toml.table, nil +} + +type parseState struct { + p *tomlParser +} + +func (d *parseState) init() { + d.p.Init() + d.p.toml.init(d.p.buffer) +} + +func (d *parseState) parse() error { + if err := d.p.Parse(); err != nil { + if err, ok := err.(*parseError); ok { + return lineError(err.Line(), errParse) + } + return err + } + return d.execute() +} + +func (d *parseState) execute() (err error) { + defer func() { + if e := recover(); e != nil { + lerr, ok := e.(*LineError) + if !ok { + panic(e) + } + err = lerr + } + }() + d.p.Execute() + return nil +} + +func (e *parseError) Line() int { + tokens := []token32{e.max} + positions, p := make([]int, 2*len(tokens)), 0 + for _, token := range tokens { + positions[p], p = int(token.begin), p+1 + positions[p], p = int(token.end), p+1 + } + for _, t := range translatePositions(e.p.buffer, positions) { + if e.p.line < t.line { + e.p.line = t.line + } + } + return e.p.line +} + +type stack struct { + key string + table *ast.Table +} + +type array struct { + parent *array + child *array + current *ast.Array + line int +} + +type toml struct { + table *ast.Table + line int + currentTable *ast.Table + s string + key string + val ast.Value + arr *array + stack []*stack + skip bool +} + +func (p *toml) init(data []rune) { + p.line = 1 + p.table = p.newTable(ast.TableTypeNormal, "") + p.table.Position.End = len(data) - 1 + p.table.Data = data[:len(data)-1] // truncate the end_symbol added by PEG parse generator. + p.currentTable = p.table +} + +func (p *toml) Error(err error) { + panic(lineError(p.line, err)) +} + +func (p *tomlParser) SetTime(begin, end int) { + p.val = &ast.Datetime{ + Position: ast.Position{Begin: begin, End: end}, + Data: p.buffer[begin:end], + Value: string(p.buffer[begin:end]), + } +} + +func (p *tomlParser) SetFloat64(begin, end int) { + p.val = &ast.Float{ + Position: ast.Position{Begin: begin, End: end}, + Data: p.buffer[begin:end], + Value: underscoreReplacer.Replace(string(p.buffer[begin:end])), + } +} + +func (p *tomlParser) SetInt64(begin, end int) { + p.val = &ast.Integer{ + Position: ast.Position{Begin: begin, End: end}, + Data: p.buffer[begin:end], + Value: underscoreReplacer.Replace(string(p.buffer[begin:end])), + } +} + +func (p *tomlParser) SetString(begin, end int) { + p.val = &ast.String{ + Position: ast.Position{Begin: begin, End: end}, + Data: p.buffer[begin:end], + Value: p.s, + } + p.s = "" +} + +func (p *tomlParser) SetBool(begin, end int) { + p.val = &ast.Boolean{ + Position: ast.Position{Begin: begin, End: end}, + Data: p.buffer[begin:end], + Value: string(p.buffer[begin:end]), + } +} + +func (p *tomlParser) StartArray() { + if p.arr == nil { + p.arr = &array{line: p.line, current: &ast.Array{}} + return + } + p.arr.child = &array{parent: p.arr, line: p.line, current: &ast.Array{}} + p.arr = p.arr.child +} + +func (p *tomlParser) AddArrayVal() { + if p.arr.current == nil { + p.arr.current = &ast.Array{} + } + p.arr.current.Value = append(p.arr.current.Value, p.val) +} + +func (p *tomlParser) SetArray(begin, end int) { + p.arr.current.Position = ast.Position{Begin: begin, End: end} + p.arr.current.Data = p.buffer[begin:end] + p.val = p.arr.current + p.arr = p.arr.parent +} + +func (p *toml) SetTable(buf []rune, begin, end int) { + p.setTable(p.table, buf, begin, end) +} + +func (p *toml) setTable(parent *ast.Table, buf []rune, begin, end int) { + name := string(buf[begin:end]) + names := splitTableKey(name) + parent, err := p.lookupTable(parent, names[:len(names)-1]) + if err != nil { + p.Error(err) + } + last := names[len(names)-1] + tbl := p.newTable(ast.TableTypeNormal, last) + switch v := parent.Fields[last].(type) { + case nil: + parent.Fields[last] = tbl + case []*ast.Table: + p.Error(fmt.Errorf("table `%s' is in conflict with array table in line %d", name, v[0].Line)) + case *ast.Table: + if (v.Position == ast.Position{}) { + // This table was created as an implicit parent. + // Replace it with the real defined table. + tbl.Fields = v.Fields + parent.Fields[last] = tbl + } else { + p.Error(fmt.Errorf("table `%s' is in conflict with table in line %d", name, v.Line)) + } + case *ast.KeyValue: + p.Error(fmt.Errorf("table `%s' is in conflict with line %d", name, v.Line)) + default: + p.Error(fmt.Errorf("BUG: table `%s' is in conflict but it's unknown type `%T'", last, v)) + } + p.currentTable = tbl +} + +func (p *toml) newTable(typ ast.TableType, name string) *ast.Table { + return &ast.Table{ + Line: p.line, + Name: name, + Type: typ, + Fields: make(map[string]interface{}), + } +} + +func (p *tomlParser) SetTableString(begin, end int) { + p.currentTable.Data = p.buffer[begin:end] + p.currentTable.Position.Begin = begin + p.currentTable.Position.End = end +} + +func (p *toml) SetArrayTable(buf []rune, begin, end int) { + p.setArrayTable(p.table, buf, begin, end) +} + +func (p *toml) setArrayTable(parent *ast.Table, buf []rune, begin, end int) { + name := string(buf[begin:end]) + names := splitTableKey(name) + parent, err := p.lookupTable(parent, names[:len(names)-1]) + if err != nil { + p.Error(err) + } + last := names[len(names)-1] + tbl := p.newTable(ast.TableTypeArray, last) + switch v := parent.Fields[last].(type) { + case nil: + parent.Fields[last] = []*ast.Table{tbl} + case []*ast.Table: + parent.Fields[last] = append(v, tbl) + case *ast.Table: + p.Error(fmt.Errorf("array table `%s' is in conflict with table in line %d", name, v.Line)) + case *ast.KeyValue: + p.Error(fmt.Errorf("array table `%s' is in conflict with line %d", name, v.Line)) + default: + p.Error(fmt.Errorf("BUG: array table `%s' is in conflict but it's unknown type `%T'", name, v)) + } + p.currentTable = tbl +} + +func (p *toml) StartInlineTable() { + p.skip = false + p.stack = append(p.stack, &stack{p.key, p.currentTable}) + buf := []rune(p.key) + if p.arr == nil { + p.setTable(p.currentTable, buf, 0, len(buf)) + } else { + p.setArrayTable(p.currentTable, buf, 0, len(buf)) + } +} + +func (p *toml) EndInlineTable() { + st := p.stack[len(p.stack)-1] + p.key, p.currentTable = st.key, st.table + p.stack[len(p.stack)-1] = nil + p.stack = p.stack[:len(p.stack)-1] + p.skip = true +} + +func (p *toml) AddLineCount(i int) { + p.line += i +} + +func (p *toml) SetKey(buf []rune, begin, end int) { + p.key = string(buf[begin:end]) +} + +func (p *toml) AddKeyValue() { + if p.skip { + p.skip = false + return + } + if val, exists := p.currentTable.Fields[p.key]; exists { + switch v := val.(type) { + case *ast.Table: + p.Error(fmt.Errorf("key `%s' is in conflict with table in line %d", p.key, v.Line)) + case *ast.KeyValue: + p.Error(fmt.Errorf("key `%s' is in conflict with line %xd", p.key, v.Line)) + default: + p.Error(fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", p.key, v)) + } + } + p.currentTable.Fields[p.key] = &ast.KeyValue{Key: p.key, Value: p.val, Line: p.line} +} + +func (p *toml) SetBasicString(buf []rune, begin, end int) { + p.s = p.unquote(string(buf[begin:end])) +} + +func (p *toml) SetMultilineString() { + p.s = p.unquote(`"` + escapeReplacer.Replace(strings.TrimLeft(p.s, "\r\n")) + `"`) +} + +func (p *toml) AddMultilineBasicBody(buf []rune, begin, end int) { + p.s += string(buf[begin:end]) +} + +func (p *toml) SetLiteralString(buf []rune, begin, end int) { + p.s = string(buf[begin:end]) +} + +func (p *toml) SetMultilineLiteralString(buf []rune, begin, end int) { + p.s = strings.TrimLeft(string(buf[begin:end]), "\r\n") +} + +func (p *toml) unquote(s string) string { + s, err := strconv.Unquote(s) + if err != nil { + p.Error(err) + } + return s +} + +func (p *toml) lookupTable(t *ast.Table, keys []string) (*ast.Table, error) { + for _, s := range keys { + val, exists := t.Fields[s] + if !exists { + tbl := p.newTable(ast.TableTypeNormal, s) + t.Fields[s] = tbl + t = tbl + continue + } + switch v := val.(type) { + case *ast.Table: + t = v + case []*ast.Table: + t = v[len(v)-1] + case *ast.KeyValue: + return nil, fmt.Errorf("key `%s' is in conflict with line %d", s, v.Line) + default: + return nil, fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", s, v) + } + } + return t, nil +} + +func splitTableKey(tk string) []string { + key := make([]byte, 0, 1) + keys := make([]string, 0, 1) + inQuote := false + for i := 0; i < len(tk); i++ { + k := tk[i] + switch { + case k == tableSeparator && !inQuote: + keys = append(keys, string(key)) + key = key[:0] // reuse buffer. + case k == '"': + inQuote = !inQuote + case (k == ' ' || k == '\t') && !inQuote: + // skip. + default: + key = append(key, k) + } + } + keys = append(keys, string(key)) + return keys +} diff --git a/vendor/github.com/naoina/toml/parse.peg b/vendor/github.com/naoina/toml/parse.peg new file mode 100644 index 000000000..da31dae30 --- /dev/null +++ b/vendor/github.com/naoina/toml/parse.peg @@ -0,0 +1,145 @@ +package toml + +type tomlParser Peg { + toml +} + +TOML <- Expression (newline Expression)* newline? !. { _ = buffer } + +Expression <- ( + <ws table ws comment? (wsnl keyval ws comment?)*> { p.SetTableString(begin, end) } + / ws keyval ws comment? + / ws comment? + / ws +) + +newline <- <[\r\n]+> { p.AddLineCount(end - begin) } + +ws <- [ \t]* +wsnl <- ( + [ \t] + / <[\r\n]> { p.AddLineCount(end - begin) } +)* + +comment <- '#' <[\t -\0x10FFFF]*> + +keyval <- key ws '=' ws val { p.AddKeyValue() } + +key <- bareKey / quotedKey + +bareKey <- <[0-9A-Za-z\-_]+> { p.SetKey(p.buffer, begin, end) } + +quotedKey <- '"' <basicChar+> '"' { p.SetKey(p.buffer, begin-1, end+1) } + +val <- ( + <datetime> { p.SetTime(begin, end) } + / <float> { p.SetFloat64(begin, end) } + / <integer> { p.SetInt64(begin, end) } + / <string> { p.SetString(begin, end) } + / <boolean> { p.SetBool(begin, end) } + / <array> { p.SetArray(begin, end) } + / inlineTable +) + +table <- stdTable / arrayTable + +stdTable <- '[' ws <tableKey> ws ']' { p.SetTable(p.buffer, begin, end) } + +arrayTable <- '[[' ws <tableKey> ws ']]' { p.SetArrayTable(p.buffer, begin, end) } + +inlineTable <- ( + '{' { p.StartInlineTable() } + ws inlineTableKeyValues ws + '}' { p.EndInlineTable() } +) + +inlineTableKeyValues <- (keyval inlineTableValSep?)* + +tableKey <- key (tableKeySep key)* + +tableKeySep <- ws '.' ws + +inlineTableValSep <- ws ',' ws + +integer <- [\-+]? int +int <- [1-9] (digit / '_' digit)+ / digit + +float <- integer (frac exp? / frac? exp) +frac <- '.' digit (digit / '_' digit)* +exp <- [eE] [\-+]? digit (digit / '_' digit)* + +string <- ( + mlLiteralString + / literalString + / mlBasicString + / basicString +) + +basicString <- <'"' basicChar* '"'> { p.SetBasicString(p.buffer, begin, end) } + +basicChar <- basicUnescaped / escaped +escaped <- escape ([btnfr"/\\] / 'u' hexQuad / 'U' hexQuad hexQuad) + +basicUnescaped <- [ -!#-\[\]-\0x10FFFF] + +escape <- '\\' + +mlBasicString <- '"""' mlBasicBody '"""' { p.SetMultilineString() } + +mlBasicBody <- ( + <basicChar / newline> { p.AddMultilineBasicBody(p.buffer, begin, end) } + / escape newline wsnl +)* + +literalString <- "'" <literalChar*> "'" { p.SetLiteralString(p.buffer, begin, end) } + +literalChar <- [\t -&(-\0x10FFFF] + +mlLiteralString <- "'''" <mlLiteralBody> "'''" { p.SetMultilineLiteralString(p.buffer, begin, end) } + +mlLiteralBody <- (!"'''" (mlLiteralChar / newline))* + +mlLiteralChar <- [\t -\0x10FFFF] + +hexdigit <- [0-9A-Fa-f] +hexQuad <- hexdigit hexdigit hexdigit hexdigit + +boolean <- 'true' / 'false' + +dateFullYear <- digitQuad +dateMonth <- digitDual +dateMDay <- digitDual +timeHour <- digitDual +timeMinute <- digitDual +timeSecond <- digitDual +timeSecfrac <- '.' digit+ +timeNumoffset <- [\-+] timeHour ':' timeMinute +timeOffset <- 'Z' / timeNumoffset +partialTime <- timeHour ':' timeMinute ':' timeSecond timeSecfrac? +fullDate <- dateFullYear '-' dateMonth '-' dateMDay +fullTime <- partialTime timeOffset +datetime <- (fullDate ('T' fullTime)?) / partialTime + +digit <- [0-9] +digitDual <- digit digit +digitQuad <- digitDual digitDual + +array <- ( + '[' { p.StartArray() } + wsnl arrayValues? wsnl + ']' +) + +arrayValues <- ( + val { p.AddArrayVal() } + ( + wsnl comment? + wsnl arraySep + wsnl comment? + wsnl val { p.AddArrayVal() } + )* + wsnl arraySep? + wsnl comment? +) + +arraySep <- ',' diff --git a/vendor/github.com/naoina/toml/parse.peg.go b/vendor/github.com/naoina/toml/parse.peg.go new file mode 100644 index 000000000..d7de73b19 --- /dev/null +++ b/vendor/github.com/naoina/toml/parse.peg.go @@ -0,0 +1,2556 @@ +package toml + +import ( + "fmt" + "math" + "sort" + "strconv" +) + +const endSymbol rune = 1114112 + +/* The rule types inferred from the grammar are below. */ +type pegRule uint8 + +const ( + ruleUnknown pegRule = iota + ruleTOML + ruleExpression + rulenewline + rulews + rulewsnl + rulecomment + rulekeyval + rulekey + rulebareKey + rulequotedKey + ruleval + ruletable + rulestdTable + rulearrayTable + ruleinlineTable + ruleinlineTableKeyValues + ruletableKey + ruletableKeySep + ruleinlineTableValSep + ruleinteger + ruleint + rulefloat + rulefrac + ruleexp + rulestring + rulebasicString + rulebasicChar + ruleescaped + rulebasicUnescaped + ruleescape + rulemlBasicString + rulemlBasicBody + ruleliteralString + ruleliteralChar + rulemlLiteralString + rulemlLiteralBody + rulemlLiteralChar + rulehexdigit + rulehexQuad + ruleboolean + ruledateFullYear + ruledateMonth + ruledateMDay + ruletimeHour + ruletimeMinute + ruletimeSecond + ruletimeSecfrac + ruletimeNumoffset + ruletimeOffset + rulepartialTime + rulefullDate + rulefullTime + ruledatetime + ruledigit + ruledigitDual + ruledigitQuad + rulearray + rulearrayValues + rulearraySep + ruleAction0 + rulePegText + ruleAction1 + ruleAction2 + ruleAction3 + ruleAction4 + ruleAction5 + ruleAction6 + ruleAction7 + ruleAction8 + ruleAction9 + ruleAction10 + ruleAction11 + ruleAction12 + ruleAction13 + ruleAction14 + ruleAction15 + ruleAction16 + ruleAction17 + ruleAction18 + ruleAction19 + ruleAction20 + ruleAction21 + ruleAction22 + ruleAction23 + ruleAction24 +) + +var rul3s = [...]string{ + "Unknown", + "TOML", + "Expression", + "newline", + "ws", + "wsnl", + "comment", + "keyval", + "key", + "bareKey", + "quotedKey", + "val", + "table", + "stdTable", + "arrayTable", + "inlineTable", + "inlineTableKeyValues", + "tableKey", + "tableKeySep", + "inlineTableValSep", + "integer", + "int", + "float", + "frac", + "exp", + "string", + "basicString", + "basicChar", + "escaped", + "basicUnescaped", + "escape", + "mlBasicString", + "mlBasicBody", + "literalString", + "literalChar", + "mlLiteralString", + "mlLiteralBody", + "mlLiteralChar", + "hexdigit", + "hexQuad", + "boolean", + "dateFullYear", + "dateMonth", + "dateMDay", + "timeHour", + "timeMinute", + "timeSecond", + "timeSecfrac", + "timeNumoffset", + "timeOffset", + "partialTime", + "fullDate", + "fullTime", + "datetime", + "digit", + "digitDual", + "digitQuad", + "array", + "arrayValues", + "arraySep", + "Action0", + "PegText", + "Action1", + "Action2", + "Action3", + "Action4", + "Action5", + "Action6", + "Action7", + "Action8", + "Action9", + "Action10", + "Action11", + "Action12", + "Action13", + "Action14", + "Action15", + "Action16", + "Action17", + "Action18", + "Action19", + "Action20", + "Action21", + "Action22", + "Action23", + "Action24", +} + +type token32 struct { + pegRule + begin, end uint32 +} + +func (t *token32) String() string { + return fmt.Sprintf("\x1B[34m%v\x1B[m %v %v", rul3s[t.pegRule], t.begin, t.end) +} + +type node32 struct { + token32 + up, next *node32 +} + +func (node *node32) print(pretty bool, buffer string) { + var print func(node *node32, depth int) + print = func(node *node32, depth int) { + for node != nil { + for c := 0; c < depth; c++ { + fmt.Printf(" ") + } + rule := rul3s[node.pegRule] + quote := strconv.Quote(string(([]rune(buffer)[node.begin:node.end]))) + if !pretty { + fmt.Printf("%v %v\n", rule, quote) + } else { + fmt.Printf("\x1B[34m%v\x1B[m %v\n", rule, quote) + } + if node.up != nil { + print(node.up, depth+1) + } + node = node.next + } + } + print(node, 0) +} + +func (node *node32) Print(buffer string) { + node.print(false, buffer) +} + +func (node *node32) PrettyPrint(buffer string) { + node.print(true, buffer) +} + +type tokens32 struct { + tree []token32 +} + +func (t *tokens32) Trim(length uint32) { + t.tree = t.tree[:length] +} + +func (t *tokens32) Print() { + for _, token := range t.tree { + fmt.Println(token.String()) + } +} + +func (t *tokens32) AST() *node32 { + type element struct { + node *node32 + down *element + } + tokens := t.Tokens() + var stack *element + for _, token := range tokens { + if token.begin == token.end { + continue + } + node := &node32{token32: token} + for stack != nil && stack.node.begin >= token.begin && stack.node.end <= token.end { + stack.node.next = node.up + node.up = stack.node + stack = stack.down + } + stack = &element{node: node, down: stack} + } + if stack != nil { + return stack.node + } + return nil +} + +func (t *tokens32) PrintSyntaxTree(buffer string) { + t.AST().Print(buffer) +} + +func (t *tokens32) PrettyPrintSyntaxTree(buffer string) { + t.AST().PrettyPrint(buffer) +} + +func (t *tokens32) Add(rule pegRule, begin, end, index uint32) { + if tree := t.tree; int(index) >= len(tree) { + expanded := make([]token32, 2*len(tree)) + copy(expanded, tree) + t.tree = expanded + } + t.tree[index] = token32{ + pegRule: rule, + begin: begin, + end: end, + } +} + +func (t *tokens32) Tokens() []token32 { + return t.tree +} + +type tomlParser struct { + toml + + Buffer string + buffer []rune + rules [86]func() bool + parse func(rule ...int) error + reset func() + Pretty bool + tokens32 +} + +func (p *tomlParser) Parse(rule ...int) error { + return p.parse(rule...) +} + +func (p *tomlParser) Reset() { + p.reset() +} + +type textPosition struct { + line, symbol int +} + +type textPositionMap map[int]textPosition + +func translatePositions(buffer []rune, positions []int) textPositionMap { + length, translations, j, line, symbol := len(positions), make(textPositionMap, len(positions)), 0, 1, 0 + sort.Ints(positions) + +search: + for i, c := range buffer { + if c == '\n' { + line, symbol = line+1, 0 + } else { + symbol++ + } + if i == positions[j] { + translations[positions[j]] = textPosition{line, symbol} + for j++; j < length; j++ { + if i != positions[j] { + continue search + } + } + break search + } + } + + return translations +} + +type parseError struct { + p *tomlParser + max token32 +} + +func (e *parseError) Error() string { + tokens, error := []token32{e.max}, "\n" + positions, p := make([]int, 2*len(tokens)), 0 + for _, token := range tokens { + positions[p], p = int(token.begin), p+1 + positions[p], p = int(token.end), p+1 + } + translations := translatePositions(e.p.buffer, positions) + format := "parse error near %v (line %v symbol %v - line %v symbol %v):\n%v\n" + if e.p.Pretty { + format = "parse error near \x1B[34m%v\x1B[m (line %v symbol %v - line %v symbol %v):\n%v\n" + } + for _, token := range tokens { + begin, end := int(token.begin), int(token.end) + error += fmt.Sprintf(format, + rul3s[token.pegRule], + translations[begin].line, translations[begin].symbol, + translations[end].line, translations[end].symbol, + strconv.Quote(string(e.p.buffer[begin:end]))) + } + + return error +} + +func (p *tomlParser) PrintSyntaxTree() { + if p.Pretty { + p.tokens32.PrettyPrintSyntaxTree(p.Buffer) + } else { + p.tokens32.PrintSyntaxTree(p.Buffer) + } +} + +func (p *tomlParser) Execute() { + buffer, _buffer, text, begin, end := p.Buffer, p.buffer, "", 0, 0 + for _, token := range p.Tokens() { + switch token.pegRule { + + case rulePegText: + begin, end = int(token.begin), int(token.end) + text = string(_buffer[begin:end]) + + case ruleAction0: + _ = buffer + case ruleAction1: + p.SetTableString(begin, end) + case ruleAction2: + p.AddLineCount(end - begin) + case ruleAction3: + p.AddLineCount(end - begin) + case ruleAction4: + p.AddKeyValue() + case ruleAction5: + p.SetKey(p.buffer, begin, end) + case ruleAction6: + p.SetKey(p.buffer, begin-1, end+1) + case ruleAction7: + p.SetTime(begin, end) + case ruleAction8: + p.SetFloat64(begin, end) + case ruleAction9: + p.SetInt64(begin, end) + case ruleAction10: + p.SetString(begin, end) + case ruleAction11: + p.SetBool(begin, end) + case ruleAction12: + p.SetArray(begin, end) + case ruleAction13: + p.SetTable(p.buffer, begin, end) + case ruleAction14: + p.SetArrayTable(p.buffer, begin, end) + case ruleAction15: + p.StartInlineTable() + case ruleAction16: + p.EndInlineTable() + case ruleAction17: + p.SetBasicString(p.buffer, begin, end) + case ruleAction18: + p.SetMultilineString() + case ruleAction19: + p.AddMultilineBasicBody(p.buffer, begin, end) + case ruleAction20: + p.SetLiteralString(p.buffer, begin, end) + case ruleAction21: + p.SetMultilineLiteralString(p.buffer, begin, end) + case ruleAction22: + p.StartArray() + case ruleAction23: + p.AddArrayVal() + case ruleAction24: + p.AddArrayVal() + + } + } + _, _, _, _, _ = buffer, _buffer, text, begin, end +} + +func (p *tomlParser) Init() { + var ( + max token32 + position, tokenIndex uint32 + buffer []rune + ) + p.reset = func() { + max = token32{} + position, tokenIndex = 0, 0 + + p.buffer = []rune(p.Buffer) + if len(p.buffer) == 0 || p.buffer[len(p.buffer)-1] != endSymbol { + p.buffer = append(p.buffer, endSymbol) + } + buffer = p.buffer + } + p.reset() + + _rules := p.rules + tree := tokens32{tree: make([]token32, math.MaxInt16)} + p.parse = func(rule ...int) error { + r := 1 + if len(rule) > 0 { + r = rule[0] + } + matches := p.rules[r]() + p.tokens32 = tree + if matches { + p.Trim(tokenIndex) + return nil + } + return &parseError{p, max} + } + + add := func(rule pegRule, begin uint32) { + tree.Add(rule, begin, position, tokenIndex) + tokenIndex++ + if begin != position && position > max.end { + max = token32{rule, begin, position} + } + } + + matchDot := func() bool { + if buffer[position] != endSymbol { + position++ + return true + } + return false + } + + /*matchChar := func(c byte) bool { + if buffer[position] == c { + position++ + return true + } + return false + }*/ + + /*matchRange := func(lower byte, upper byte) bool { + if c := buffer[position]; c >= lower && c <= upper { + position++ + return true + } + return false + }*/ + + _rules = [...]func() bool{ + nil, + /* 0 TOML <- <(Expression (newline Expression)* newline? !. Action0)> */ + func() bool { + position0, tokenIndex0 := position, tokenIndex + { + position1 := position + if !_rules[ruleExpression]() { + goto l0 + } + l2: + { + position3, tokenIndex3 := position, tokenIndex + if !_rules[rulenewline]() { + goto l3 + } + if !_rules[ruleExpression]() { + goto l3 + } + goto l2 + l3: + position, tokenIndex = position3, tokenIndex3 + } + { + position4, tokenIndex4 := position, tokenIndex + if !_rules[rulenewline]() { + goto l4 + } + goto l5 + l4: + position, tokenIndex = position4, tokenIndex4 + } + l5: + { + position6, tokenIndex6 := position, tokenIndex + if !matchDot() { + goto l6 + } + goto l0 + l6: + position, tokenIndex = position6, tokenIndex6 + } + { + add(ruleAction0, position) + } + add(ruleTOML, position1) + } + return true + l0: + position, tokenIndex = position0, tokenIndex0 + return false + }, + /* 1 Expression <- <((<(ws table ws comment? (wsnl keyval ws comment?)*)> Action1) / (ws keyval ws comment?) / (ws comment?) / ws)> */ + func() bool { + position8, tokenIndex8 := position, tokenIndex + { + position9 := position + { + position10, tokenIndex10 := position, tokenIndex + { + position12 := position + if !_rules[rulews]() { + goto l11 + } + { + position13 := position + { + position14, tokenIndex14 := position, tokenIndex + { + position16 := position + if buffer[position] != rune('[') { + goto l15 + } + position++ + if !_rules[rulews]() { + goto l15 + } + { + position17 := position + if !_rules[ruletableKey]() { + goto l15 + } + add(rulePegText, position17) + } + if !_rules[rulews]() { + goto l15 + } + if buffer[position] != rune(']') { + goto l15 + } + position++ + { + add(ruleAction13, position) + } + add(rulestdTable, position16) + } + goto l14 + l15: + position, tokenIndex = position14, tokenIndex14 + { + position19 := position + if buffer[position] != rune('[') { + goto l11 + } + position++ + if buffer[position] != rune('[') { + goto l11 + } + position++ + if !_rules[rulews]() { + goto l11 + } + { + position20 := position + if !_rules[ruletableKey]() { + goto l11 + } + add(rulePegText, position20) + } + if !_rules[rulews]() { + goto l11 + } + if buffer[position] != rune(']') { + goto l11 + } + position++ + if buffer[position] != rune(']') { + goto l11 + } + position++ + { + add(ruleAction14, position) + } + add(rulearrayTable, position19) + } + } + l14: + add(ruletable, position13) + } + if !_rules[rulews]() { + goto l11 + } + { + position22, tokenIndex22 := position, tokenIndex + if !_rules[rulecomment]() { + goto l22 + } + goto l23 + l22: + position, tokenIndex = position22, tokenIndex22 + } + l23: + l24: + { + position25, tokenIndex25 := position, tokenIndex + if !_rules[rulewsnl]() { + goto l25 + } + if !_rules[rulekeyval]() { + goto l25 + } + if !_rules[rulews]() { + goto l25 + } + { + position26, tokenIndex26 := position, tokenIndex + if !_rules[rulecomment]() { + goto l26 + } + goto l27 + l26: + position, tokenIndex = position26, tokenIndex26 + } + l27: + goto l24 + l25: + position, tokenIndex = position25, tokenIndex25 + } + add(rulePegText, position12) + } + { + add(ruleAction1, position) + } + goto l10 + l11: + position, tokenIndex = position10, tokenIndex10 + if !_rules[rulews]() { + goto l29 + } + if !_rules[rulekeyval]() { + goto l29 + } + if !_rules[rulews]() { + goto l29 + } + { + position30, tokenIndex30 := position, tokenIndex + if !_rules[rulecomment]() { + goto l30 + } + goto l31 + l30: + position, tokenIndex = position30, tokenIndex30 + } + l31: + goto l10 + l29: + position, tokenIndex = position10, tokenIndex10 + if !_rules[rulews]() { + goto l32 + } + { + position33, tokenIndex33 := position, tokenIndex + if !_rules[rulecomment]() { + goto l33 + } + goto l34 + l33: + position, tokenIndex = position33, tokenIndex33 + } + l34: + goto l10 + l32: + position, tokenIndex = position10, tokenIndex10 + if !_rules[rulews]() { + goto l8 + } + } + l10: + add(ruleExpression, position9) + } + return true + l8: + position, tokenIndex = position8, tokenIndex8 + return false + }, + /* 2 newline <- <(<('\r' / '\n')+> Action2)> */ + func() bool { + position35, tokenIndex35 := position, tokenIndex + { + position36 := position + { + position37 := position + { + position40, tokenIndex40 := position, tokenIndex + if buffer[position] != rune('\r') { + goto l41 + } + position++ + goto l40 + l41: + position, tokenIndex = position40, tokenIndex40 + if buffer[position] != rune('\n') { + goto l35 + } + position++ + } + l40: + l38: + { + position39, tokenIndex39 := position, tokenIndex + { + position42, tokenIndex42 := position, tokenIndex + if buffer[position] != rune('\r') { + goto l43 + } + position++ + goto l42 + l43: + position, tokenIndex = position42, tokenIndex42 + if buffer[position] != rune('\n') { + goto l39 + } + position++ + } + l42: + goto l38 + l39: + position, tokenIndex = position39, tokenIndex39 + } + add(rulePegText, position37) + } + { + add(ruleAction2, position) + } + add(rulenewline, position36) + } + return true + l35: + position, tokenIndex = position35, tokenIndex35 + return false + }, + /* 3 ws <- <(' ' / '\t')*> */ + func() bool { + { + position46 := position + l47: + { + position48, tokenIndex48 := position, tokenIndex + { + position49, tokenIndex49 := position, tokenIndex + if buffer[position] != rune(' ') { + goto l50 + } + position++ + goto l49 + l50: + position, tokenIndex = position49, tokenIndex49 + if buffer[position] != rune('\t') { + goto l48 + } + position++ + } + l49: + goto l47 + l48: + position, tokenIndex = position48, tokenIndex48 + } + add(rulews, position46) + } + return true + }, + /* 4 wsnl <- <((&('\t') '\t') | (&(' ') ' ') | (&('\n' | '\r') (<('\r' / '\n')> Action3)))*> */ + func() bool { + { + position52 := position + l53: + { + position54, tokenIndex54 := position, tokenIndex + { + switch buffer[position] { + case '\t': + if buffer[position] != rune('\t') { + goto l54 + } + position++ + break + case ' ': + if buffer[position] != rune(' ') { + goto l54 + } + position++ + break + default: + { + position56 := position + { + position57, tokenIndex57 := position, tokenIndex + if buffer[position] != rune('\r') { + goto l58 + } + position++ + goto l57 + l58: + position, tokenIndex = position57, tokenIndex57 + if buffer[position] != rune('\n') { + goto l54 + } + position++ + } + l57: + add(rulePegText, position56) + } + { + add(ruleAction3, position) + } + break + } + } + + goto l53 + l54: + position, tokenIndex = position54, tokenIndex54 + } + add(rulewsnl, position52) + } + return true + }, + /* 5 comment <- <('#' <('\t' / [ -\U0010ffff])*>)> */ + func() bool { + position60, tokenIndex60 := position, tokenIndex + { + position61 := position + if buffer[position] != rune('#') { + goto l60 + } + position++ + { + position62 := position + l63: + { + position64, tokenIndex64 := position, tokenIndex + { + position65, tokenIndex65 := position, tokenIndex + if buffer[position] != rune('\t') { + goto l66 + } + position++ + goto l65 + l66: + position, tokenIndex = position65, tokenIndex65 + if c := buffer[position]; c < rune(' ') || c > rune('\U0010ffff') { + goto l64 + } + position++ + } + l65: + goto l63 + l64: + position, tokenIndex = position64, tokenIndex64 + } + add(rulePegText, position62) + } + add(rulecomment, position61) + } + return true + l60: + position, tokenIndex = position60, tokenIndex60 + return false + }, + /* 6 keyval <- <(key ws '=' ws val Action4)> */ + func() bool { + position67, tokenIndex67 := position, tokenIndex + { + position68 := position + if !_rules[rulekey]() { + goto l67 + } + if !_rules[rulews]() { + goto l67 + } + if buffer[position] != rune('=') { + goto l67 + } + position++ + if !_rules[rulews]() { + goto l67 + } + if !_rules[ruleval]() { + goto l67 + } + { + add(ruleAction4, position) + } + add(rulekeyval, position68) + } + return true + l67: + position, tokenIndex = position67, tokenIndex67 + return false + }, + /* 7 key <- <(bareKey / quotedKey)> */ + func() bool { + position70, tokenIndex70 := position, tokenIndex + { + position71 := position + { + position72, tokenIndex72 := position, tokenIndex + { + position74 := position + { + position75 := position + { + switch buffer[position] { + case '_': + if buffer[position] != rune('_') { + goto l73 + } + position++ + break + case '-': + if buffer[position] != rune('-') { + goto l73 + } + position++ + break + case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z': + if c := buffer[position]; c < rune('a') || c > rune('z') { + goto l73 + } + position++ + break + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + if c := buffer[position]; c < rune('0') || c > rune('9') { + goto l73 + } + position++ + break + default: + if c := buffer[position]; c < rune('A') || c > rune('Z') { + goto l73 + } + position++ + break + } + } + + l76: + { + position77, tokenIndex77 := position, tokenIndex + { + switch buffer[position] { + case '_': + if buffer[position] != rune('_') { + goto l77 + } + position++ + break + case '-': + if buffer[position] != rune('-') { + goto l77 + } + position++ + break + case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z': + if c := buffer[position]; c < rune('a') || c > rune('z') { + goto l77 + } + position++ + break + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + if c := buffer[position]; c < rune('0') || c > rune('9') { + goto l77 + } + position++ + break + default: + if c := buffer[position]; c < rune('A') || c > rune('Z') { + goto l77 + } + position++ + break + } + } + + goto l76 + l77: + position, tokenIndex = position77, tokenIndex77 + } + add(rulePegText, position75) + } + { + add(ruleAction5, position) + } + add(rulebareKey, position74) + } + goto l72 + l73: + position, tokenIndex = position72, tokenIndex72 + { + position81 := position + if buffer[position] != rune('"') { + goto l70 + } + position++ + { + position82 := position + if !_rules[rulebasicChar]() { + goto l70 + } + l83: + { + position84, tokenIndex84 := position, tokenIndex + if !_rules[rulebasicChar]() { + goto l84 + } + goto l83 + l84: + position, tokenIndex = position84, tokenIndex84 + } + add(rulePegText, position82) + } + if buffer[position] != rune('"') { + goto l70 + } + position++ + { + add(ruleAction6, position) + } + add(rulequotedKey, position81) + } + } + l72: + add(rulekey, position71) + } + return true + l70: + position, tokenIndex = position70, tokenIndex70 + return false + }, + /* 8 bareKey <- <(<((&('_') '_') | (&('-') '-') | (&('a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z') [a-z]) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') [0-9]) | (&('A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z') [A-Z]))+> Action5)> */ + nil, + /* 9 quotedKey <- <('"' <basicChar+> '"' Action6)> */ + nil, + /* 10 val <- <((<datetime> Action7) / (<float> Action8) / ((&('{') inlineTable) | (&('[') (<array> Action12)) | (&('f' | 't') (<boolean> Action11)) | (&('"' | '\'') (<string> Action10)) | (&('+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') (<integer> Action9))))> */ + func() bool { + position88, tokenIndex88 := position, tokenIndex + { + position89 := position + { + position90, tokenIndex90 := position, tokenIndex + { + position92 := position + { + position93 := position + { + position94, tokenIndex94 := position, tokenIndex + { + position96 := position + { + position97 := position + { + position98 := position + if !_rules[ruledigitDual]() { + goto l95 + } + if !_rules[ruledigitDual]() { + goto l95 + } + add(ruledigitQuad, position98) + } + add(ruledateFullYear, position97) + } + if buffer[position] != rune('-') { + goto l95 + } + position++ + { + position99 := position + if !_rules[ruledigitDual]() { + goto l95 + } + add(ruledateMonth, position99) + } + if buffer[position] != rune('-') { + goto l95 + } + position++ + { + position100 := position + if !_rules[ruledigitDual]() { + goto l95 + } + add(ruledateMDay, position100) + } + add(rulefullDate, position96) + } + { + position101, tokenIndex101 := position, tokenIndex + if buffer[position] != rune('T') { + goto l101 + } + position++ + { + position103 := position + if !_rules[rulepartialTime]() { + goto l101 + } + { + position104 := position + { + position105, tokenIndex105 := position, tokenIndex + if buffer[position] != rune('Z') { + goto l106 + } + position++ + goto l105 + l106: + position, tokenIndex = position105, tokenIndex105 + { + position107 := position + { + position108, tokenIndex108 := position, tokenIndex + if buffer[position] != rune('-') { + goto l109 + } + position++ + goto l108 + l109: + position, tokenIndex = position108, tokenIndex108 + if buffer[position] != rune('+') { + goto l101 + } + position++ + } + l108: + if !_rules[ruletimeHour]() { + goto l101 + } + if buffer[position] != rune(':') { + goto l101 + } + position++ + if !_rules[ruletimeMinute]() { + goto l101 + } + add(ruletimeNumoffset, position107) + } + } + l105: + add(ruletimeOffset, position104) + } + add(rulefullTime, position103) + } + goto l102 + l101: + position, tokenIndex = position101, tokenIndex101 + } + l102: + goto l94 + l95: + position, tokenIndex = position94, tokenIndex94 + if !_rules[rulepartialTime]() { + goto l91 + } + } + l94: + add(ruledatetime, position93) + } + add(rulePegText, position92) + } + { + add(ruleAction7, position) + } + goto l90 + l91: + position, tokenIndex = position90, tokenIndex90 + { + position112 := position + { + position113 := position + if !_rules[ruleinteger]() { + goto l111 + } + { + position114, tokenIndex114 := position, tokenIndex + if !_rules[rulefrac]() { + goto l115 + } + { + position116, tokenIndex116 := position, tokenIndex + if !_rules[ruleexp]() { + goto l116 + } + goto l117 + l116: + position, tokenIndex = position116, tokenIndex116 + } + l117: + goto l114 + l115: + position, tokenIndex = position114, tokenIndex114 + { + position118, tokenIndex118 := position, tokenIndex + if !_rules[rulefrac]() { + goto l118 + } + goto l119 + l118: + position, tokenIndex = position118, tokenIndex118 + } + l119: + if !_rules[ruleexp]() { + goto l111 + } + } + l114: + add(rulefloat, position113) + } + add(rulePegText, position112) + } + { + add(ruleAction8, position) + } + goto l90 + l111: + position, tokenIndex = position90, tokenIndex90 + { + switch buffer[position] { + case '{': + { + position122 := position + if buffer[position] != rune('{') { + goto l88 + } + position++ + { + add(ruleAction15, position) + } + if !_rules[rulews]() { + goto l88 + } + { + position124 := position + l125: + { + position126, tokenIndex126 := position, tokenIndex + if !_rules[rulekeyval]() { + goto l126 + } + { + position127, tokenIndex127 := position, tokenIndex + { + position129 := position + if !_rules[rulews]() { + goto l127 + } + if buffer[position] != rune(',') { + goto l127 + } + position++ + if !_rules[rulews]() { + goto l127 + } + add(ruleinlineTableValSep, position129) + } + goto l128 + l127: + position, tokenIndex = position127, tokenIndex127 + } + l128: + goto l125 + l126: + position, tokenIndex = position126, tokenIndex126 + } + add(ruleinlineTableKeyValues, position124) + } + if !_rules[rulews]() { + goto l88 + } + if buffer[position] != rune('}') { + goto l88 + } + position++ + { + add(ruleAction16, position) + } + add(ruleinlineTable, position122) + } + break + case '[': + { + position131 := position + { + position132 := position + if buffer[position] != rune('[') { + goto l88 + } + position++ + { + add(ruleAction22, position) + } + if !_rules[rulewsnl]() { + goto l88 + } + { + position134, tokenIndex134 := position, tokenIndex + { + position136 := position + if !_rules[ruleval]() { + goto l134 + } + { + add(ruleAction23, position) + } + l138: + { + position139, tokenIndex139 := position, tokenIndex + if !_rules[rulewsnl]() { + goto l139 + } + { + position140, tokenIndex140 := position, tokenIndex + if !_rules[rulecomment]() { + goto l140 + } + goto l141 + l140: + position, tokenIndex = position140, tokenIndex140 + } + l141: + if !_rules[rulewsnl]() { + goto l139 + } + if !_rules[rulearraySep]() { + goto l139 + } + if !_rules[rulewsnl]() { + goto l139 + } + { + position142, tokenIndex142 := position, tokenIndex + if !_rules[rulecomment]() { + goto l142 + } + goto l143 + l142: + position, tokenIndex = position142, tokenIndex142 + } + l143: + if !_rules[rulewsnl]() { + goto l139 + } + if !_rules[ruleval]() { + goto l139 + } + { + add(ruleAction24, position) + } + goto l138 + l139: + position, tokenIndex = position139, tokenIndex139 + } + if !_rules[rulewsnl]() { + goto l134 + } + { + position145, tokenIndex145 := position, tokenIndex + if !_rules[rulearraySep]() { + goto l145 + } + goto l146 + l145: + position, tokenIndex = position145, tokenIndex145 + } + l146: + if !_rules[rulewsnl]() { + goto l134 + } + { + position147, tokenIndex147 := position, tokenIndex + if !_rules[rulecomment]() { + goto l147 + } + goto l148 + l147: + position, tokenIndex = position147, tokenIndex147 + } + l148: + add(rulearrayValues, position136) + } + goto l135 + l134: + position, tokenIndex = position134, tokenIndex134 + } + l135: + if !_rules[rulewsnl]() { + goto l88 + } + if buffer[position] != rune(']') { + goto l88 + } + position++ + add(rulearray, position132) + } + add(rulePegText, position131) + } + { + add(ruleAction12, position) + } + break + case 'f', 't': + { + position150 := position + { + position151 := position + { + position152, tokenIndex152 := position, tokenIndex + if buffer[position] != rune('t') { + goto l153 + } + position++ + if buffer[position] != rune('r') { + goto l153 + } + position++ + if buffer[position] != rune('u') { + goto l153 + } + position++ + if buffer[position] != rune('e') { + goto l153 + } + position++ + goto l152 + l153: + position, tokenIndex = position152, tokenIndex152 + if buffer[position] != rune('f') { + goto l88 + } + position++ + if buffer[position] != rune('a') { + goto l88 + } + position++ + if buffer[position] != rune('l') { + goto l88 + } + position++ + if buffer[position] != rune('s') { + goto l88 + } + position++ + if buffer[position] != rune('e') { + goto l88 + } + position++ + } + l152: + add(ruleboolean, position151) + } + add(rulePegText, position150) + } + { + add(ruleAction11, position) + } + break + case '"', '\'': + { + position155 := position + { + position156 := position + { + position157, tokenIndex157 := position, tokenIndex + { + position159 := position + if buffer[position] != rune('\'') { + goto l158 + } + position++ + if buffer[position] != rune('\'') { + goto l158 + } + position++ + if buffer[position] != rune('\'') { + goto l158 + } + position++ + { + position160 := position + { + position161 := position + l162: + { + position163, tokenIndex163 := position, tokenIndex + { + position164, tokenIndex164 := position, tokenIndex + if buffer[position] != rune('\'') { + goto l164 + } + position++ + if buffer[position] != rune('\'') { + goto l164 + } + position++ + if buffer[position] != rune('\'') { + goto l164 + } + position++ + goto l163 + l164: + position, tokenIndex = position164, tokenIndex164 + } + { + position165, tokenIndex165 := position, tokenIndex + { + position167 := position + { + position168, tokenIndex168 := position, tokenIndex + if buffer[position] != rune('\t') { + goto l169 + } + position++ + goto l168 + l169: + position, tokenIndex = position168, tokenIndex168 + if c := buffer[position]; c < rune(' ') || c > rune('\U0010ffff') { + goto l166 + } + position++ + } + l168: + add(rulemlLiteralChar, position167) + } + goto l165 + l166: + position, tokenIndex = position165, tokenIndex165 + if !_rules[rulenewline]() { + goto l163 + } + } + l165: + goto l162 + l163: + position, tokenIndex = position163, tokenIndex163 + } + add(rulemlLiteralBody, position161) + } + add(rulePegText, position160) + } + if buffer[position] != rune('\'') { + goto l158 + } + position++ + if buffer[position] != rune('\'') { + goto l158 + } + position++ + if buffer[position] != rune('\'') { + goto l158 + } + position++ + { + add(ruleAction21, position) + } + add(rulemlLiteralString, position159) + } + goto l157 + l158: + position, tokenIndex = position157, tokenIndex157 + { + position172 := position + if buffer[position] != rune('\'') { + goto l171 + } + position++ + { + position173 := position + l174: + { + position175, tokenIndex175 := position, tokenIndex + { + position176 := position + { + switch buffer[position] { + case '\t': + if buffer[position] != rune('\t') { + goto l175 + } + position++ + break + case ' ', '!', '"', '#', '$', '%', '&': + if c := buffer[position]; c < rune(' ') || c > rune('&') { + goto l175 + } + position++ + break + default: + if c := buffer[position]; c < rune('(') || c > rune('\U0010ffff') { + goto l175 + } + position++ + break + } + } + + add(ruleliteralChar, position176) + } + goto l174 + l175: + position, tokenIndex = position175, tokenIndex175 + } + add(rulePegText, position173) + } + if buffer[position] != rune('\'') { + goto l171 + } + position++ + { + add(ruleAction20, position) + } + add(ruleliteralString, position172) + } + goto l157 + l171: + position, tokenIndex = position157, tokenIndex157 + { + position180 := position + if buffer[position] != rune('"') { + goto l179 + } + position++ + if buffer[position] != rune('"') { + goto l179 + } + position++ + if buffer[position] != rune('"') { + goto l179 + } + position++ + { + position181 := position + l182: + { + position183, tokenIndex183 := position, tokenIndex + { + position184, tokenIndex184 := position, tokenIndex + { + position186 := position + { + position187, tokenIndex187 := position, tokenIndex + if !_rules[rulebasicChar]() { + goto l188 + } + goto l187 + l188: + position, tokenIndex = position187, tokenIndex187 + if !_rules[rulenewline]() { + goto l185 + } + } + l187: + add(rulePegText, position186) + } + { + add(ruleAction19, position) + } + goto l184 + l185: + position, tokenIndex = position184, tokenIndex184 + if !_rules[ruleescape]() { + goto l183 + } + if !_rules[rulenewline]() { + goto l183 + } + if !_rules[rulewsnl]() { + goto l183 + } + } + l184: + goto l182 + l183: + position, tokenIndex = position183, tokenIndex183 + } + add(rulemlBasicBody, position181) + } + if buffer[position] != rune('"') { + goto l179 + } + position++ + if buffer[position] != rune('"') { + goto l179 + } + position++ + if buffer[position] != rune('"') { + goto l179 + } + position++ + { + add(ruleAction18, position) + } + add(rulemlBasicString, position180) + } + goto l157 + l179: + position, tokenIndex = position157, tokenIndex157 + { + position191 := position + { + position192 := position + if buffer[position] != rune('"') { + goto l88 + } + position++ + l193: + { + position194, tokenIndex194 := position, tokenIndex + if !_rules[rulebasicChar]() { + goto l194 + } + goto l193 + l194: + position, tokenIndex = position194, tokenIndex194 + } + if buffer[position] != rune('"') { + goto l88 + } + position++ + add(rulePegText, position192) + } + { + add(ruleAction17, position) + } + add(rulebasicString, position191) + } + } + l157: + add(rulestring, position156) + } + add(rulePegText, position155) + } + { + add(ruleAction10, position) + } + break + default: + { + position197 := position + if !_rules[ruleinteger]() { + goto l88 + } + add(rulePegText, position197) + } + { + add(ruleAction9, position) + } + break + } + } + + } + l90: + add(ruleval, position89) + } + return true + l88: + position, tokenIndex = position88, tokenIndex88 + return false + }, + /* 11 table <- <(stdTable / arrayTable)> */ + nil, + /* 12 stdTable <- <('[' ws <tableKey> ws ']' Action13)> */ + nil, + /* 13 arrayTable <- <('[' '[' ws <tableKey> ws (']' ']') Action14)> */ + nil, + /* 14 inlineTable <- <('{' Action15 ws inlineTableKeyValues ws '}' Action16)> */ + nil, + /* 15 inlineTableKeyValues <- <(keyval inlineTableValSep?)*> */ + nil, + /* 16 tableKey <- <(key (tableKeySep key)*)> */ + func() bool { + position204, tokenIndex204 := position, tokenIndex + { + position205 := position + if !_rules[rulekey]() { + goto l204 + } + l206: + { + position207, tokenIndex207 := position, tokenIndex + { + position208 := position + if !_rules[rulews]() { + goto l207 + } + if buffer[position] != rune('.') { + goto l207 + } + position++ + if !_rules[rulews]() { + goto l207 + } + add(ruletableKeySep, position208) + } + if !_rules[rulekey]() { + goto l207 + } + goto l206 + l207: + position, tokenIndex = position207, tokenIndex207 + } + add(ruletableKey, position205) + } + return true + l204: + position, tokenIndex = position204, tokenIndex204 + return false + }, + /* 17 tableKeySep <- <(ws '.' ws)> */ + nil, + /* 18 inlineTableValSep <- <(ws ',' ws)> */ + nil, + /* 19 integer <- <(('-' / '+')? int)> */ + func() bool { + position211, tokenIndex211 := position, tokenIndex + { + position212 := position + { + position213, tokenIndex213 := position, tokenIndex + { + position215, tokenIndex215 := position, tokenIndex + if buffer[position] != rune('-') { + goto l216 + } + position++ + goto l215 + l216: + position, tokenIndex = position215, tokenIndex215 + if buffer[position] != rune('+') { + goto l213 + } + position++ + } + l215: + goto l214 + l213: + position, tokenIndex = position213, tokenIndex213 + } + l214: + { + position217 := position + { + position218, tokenIndex218 := position, tokenIndex + if c := buffer[position]; c < rune('1') || c > rune('9') { + goto l219 + } + position++ + { + position222, tokenIndex222 := position, tokenIndex + if !_rules[ruledigit]() { + goto l223 + } + goto l222 + l223: + position, tokenIndex = position222, tokenIndex222 + if buffer[position] != rune('_') { + goto l219 + } + position++ + if !_rules[ruledigit]() { + goto l219 + } + } + l222: + l220: + { + position221, tokenIndex221 := position, tokenIndex + { + position224, tokenIndex224 := position, tokenIndex + if !_rules[ruledigit]() { + goto l225 + } + goto l224 + l225: + position, tokenIndex = position224, tokenIndex224 + if buffer[position] != rune('_') { + goto l221 + } + position++ + if !_rules[ruledigit]() { + goto l221 + } + } + l224: + goto l220 + l221: + position, tokenIndex = position221, tokenIndex221 + } + goto l218 + l219: + position, tokenIndex = position218, tokenIndex218 + if !_rules[ruledigit]() { + goto l211 + } + } + l218: + add(ruleint, position217) + } + add(ruleinteger, position212) + } + return true + l211: + position, tokenIndex = position211, tokenIndex211 + return false + }, + /* 20 int <- <(([1-9] (digit / ('_' digit))+) / digit)> */ + nil, + /* 21 float <- <(integer ((frac exp?) / (frac? exp)))> */ + nil, + /* 22 frac <- <('.' digit (digit / ('_' digit))*)> */ + func() bool { + position228, tokenIndex228 := position, tokenIndex + { + position229 := position + if buffer[position] != rune('.') { + goto l228 + } + position++ + if !_rules[ruledigit]() { + goto l228 + } + l230: + { + position231, tokenIndex231 := position, tokenIndex + { + position232, tokenIndex232 := position, tokenIndex + if !_rules[ruledigit]() { + goto l233 + } + goto l232 + l233: + position, tokenIndex = position232, tokenIndex232 + if buffer[position] != rune('_') { + goto l231 + } + position++ + if !_rules[ruledigit]() { + goto l231 + } + } + l232: + goto l230 + l231: + position, tokenIndex = position231, tokenIndex231 + } + add(rulefrac, position229) + } + return true + l228: + position, tokenIndex = position228, tokenIndex228 + return false + }, + /* 23 exp <- <(('e' / 'E') ('-' / '+')? digit (digit / ('_' digit))*)> */ + func() bool { + position234, tokenIndex234 := position, tokenIndex + { + position235 := position + { + position236, tokenIndex236 := position, tokenIndex + if buffer[position] != rune('e') { + goto l237 + } + position++ + goto l236 + l237: + position, tokenIndex = position236, tokenIndex236 + if buffer[position] != rune('E') { + goto l234 + } + position++ + } + l236: + { + position238, tokenIndex238 := position, tokenIndex + { + position240, tokenIndex240 := position, tokenIndex + if buffer[position] != rune('-') { + goto l241 + } + position++ + goto l240 + l241: + position, tokenIndex = position240, tokenIndex240 + if buffer[position] != rune('+') { + goto l238 + } + position++ + } + l240: + goto l239 + l238: + position, tokenIndex = position238, tokenIndex238 + } + l239: + if !_rules[ruledigit]() { + goto l234 + } + l242: + { + position243, tokenIndex243 := position, tokenIndex + { + position244, tokenIndex244 := position, tokenIndex + if !_rules[ruledigit]() { + goto l245 + } + goto l244 + l245: + position, tokenIndex = position244, tokenIndex244 + if buffer[position] != rune('_') { + goto l243 + } + position++ + if !_rules[ruledigit]() { + goto l243 + } + } + l244: + goto l242 + l243: + position, tokenIndex = position243, tokenIndex243 + } + add(ruleexp, position235) + } + return true + l234: + position, tokenIndex = position234, tokenIndex234 + return false + }, + /* 24 string <- <(mlLiteralString / literalString / mlBasicString / basicString)> */ + nil, + /* 25 basicString <- <(<('"' basicChar* '"')> Action17)> */ + nil, + /* 26 basicChar <- <(basicUnescaped / escaped)> */ + func() bool { + position248, tokenIndex248 := position, tokenIndex + { + position249 := position + { + position250, tokenIndex250 := position, tokenIndex + { + position252 := position + { + switch buffer[position] { + case ' ', '!': + if c := buffer[position]; c < rune(' ') || c > rune('!') { + goto l251 + } + position++ + break + case '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[': + if c := buffer[position]; c < rune('#') || c > rune('[') { + goto l251 + } + position++ + break + default: + if c := buffer[position]; c < rune(']') || c > rune('\U0010ffff') { + goto l251 + } + position++ + break + } + } + + add(rulebasicUnescaped, position252) + } + goto l250 + l251: + position, tokenIndex = position250, tokenIndex250 + { + position254 := position + if !_rules[ruleescape]() { + goto l248 + } + { + switch buffer[position] { + case 'U': + if buffer[position] != rune('U') { + goto l248 + } + position++ + if !_rules[rulehexQuad]() { + goto l248 + } + if !_rules[rulehexQuad]() { + goto l248 + } + break + case 'u': + if buffer[position] != rune('u') { + goto l248 + } + position++ + if !_rules[rulehexQuad]() { + goto l248 + } + break + case '\\': + if buffer[position] != rune('\\') { + goto l248 + } + position++ + break + case '/': + if buffer[position] != rune('/') { + goto l248 + } + position++ + break + case '"': + if buffer[position] != rune('"') { + goto l248 + } + position++ + break + case 'r': + if buffer[position] != rune('r') { + goto l248 + } + position++ + break + case 'f': + if buffer[position] != rune('f') { + goto l248 + } + position++ + break + case 'n': + if buffer[position] != rune('n') { + goto l248 + } + position++ + break + case 't': + if buffer[position] != rune('t') { + goto l248 + } + position++ + break + default: + if buffer[position] != rune('b') { + goto l248 + } + position++ + break + } + } + + add(ruleescaped, position254) + } + } + l250: + add(rulebasicChar, position249) + } + return true + l248: + position, tokenIndex = position248, tokenIndex248 + return false + }, + /* 27 escaped <- <(escape ((&('U') ('U' hexQuad hexQuad)) | (&('u') ('u' hexQuad)) | (&('\\') '\\') | (&('/') '/') | (&('"') '"') | (&('r') 'r') | (&('f') 'f') | (&('n') 'n') | (&('t') 't') | (&('b') 'b')))> */ + nil, + /* 28 basicUnescaped <- <((&(' ' | '!') [ -!]) | (&('#' | '$' | '%' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '[') [#-[]) | (&(']' | '^' | '_' | '`' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | '{' | '|' | '}' | '~' | '\u007f' | '\u0080' | '\u0081' | '\u0082' | '\u0083' | '\u0084' | '\u0085' | '\u0086' | '\u0087' | '\u0088' | '\u0089' | '\u008a' | '\u008b' | '\u008c' | '\u008d' | '\u008e' | '\u008f' | '\u0090' | '\u0091' | '\u0092' | '\u0093' | '\u0094' | '\u0095' | '\u0096' | '\u0097' | '\u0098' | '\u0099' | '\u009a' | '\u009b' | '\u009c' | '\u009d' | '\u009e' | '\u009f' | '\u00a0' | '¡' | '¢' | '£' | '¤' | '¥' | '¦' | '§' | '¨' | '©' | 'ª' | '«' | '¬' | '\u00ad' | '®' | '¯' | '°' | '±' | '²' | '³' | '´' | 'µ' | '¶' | '·' | '¸' | '¹' | 'º' | '»' | '¼' | '½' | '¾' | '¿' | 'À' | 'Á' | 'Â' | 'Ã' | 'Ä' | 'Å' | 'Æ' | 'Ç' | 'È' | 'É' | 'Ê' | 'Ë' | 'Ì' | 'Í' | 'Î' | 'Ï' | 'Ð' | 'Ñ' | 'Ò' | 'Ó' | 'Ô' | 'Õ' | 'Ö' | '×' | 'Ø' | 'Ù' | 'Ú' | 'Û' | 'Ü' | 'Ý' | 'Þ' | 'ß' | 'à' | 'á' | 'â' | 'ã' | 'ä' | 'å' | 'æ' | 'ç' | 'è' | 'é' | 'ê' | 'ë' | 'ì' | 'í' | 'î' | 'ï' | 'ð' | 'ñ' | 'ò' | 'ó' | 'ô' | 'õ' | 'ö' | '÷' | 'ø' | 'ù' | 'ú' | 'û' | 'ü' | 'ý' | 'þ' | 'ÿ') []-\U0010ffff]))> */ + nil, + /* 29 escape <- <'\\'> */ + func() bool { + position258, tokenIndex258 := position, tokenIndex + { + position259 := position + if buffer[position] != rune('\\') { + goto l258 + } + position++ + add(ruleescape, position259) + } + return true + l258: + position, tokenIndex = position258, tokenIndex258 + return false + }, + /* 30 mlBasicString <- <('"' '"' '"' mlBasicBody ('"' '"' '"') Action18)> */ + nil, + /* 31 mlBasicBody <- <((<(basicChar / newline)> Action19) / (escape newline wsnl))*> */ + nil, + /* 32 literalString <- <('\'' <literalChar*> '\'' Action20)> */ + nil, + /* 33 literalChar <- <((&('\t') '\t') | (&(' ' | '!' | '"' | '#' | '$' | '%' | '&') [ -&]) | (&('(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '[' | '\\' | ']' | '^' | '_' | '`' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | '{' | '|' | '}' | '~' | '\u007f' | '\u0080' | '\u0081' | '\u0082' | '\u0083' | '\u0084' | '\u0085' | '\u0086' | '\u0087' | '\u0088' | '\u0089' | '\u008a' | '\u008b' | '\u008c' | '\u008d' | '\u008e' | '\u008f' | '\u0090' | '\u0091' | '\u0092' | '\u0093' | '\u0094' | '\u0095' | '\u0096' | '\u0097' | '\u0098' | '\u0099' | '\u009a' | '\u009b' | '\u009c' | '\u009d' | '\u009e' | '\u009f' | '\u00a0' | '¡' | '¢' | '£' | '¤' | '¥' | '¦' | '§' | '¨' | '©' | 'ª' | '«' | '¬' | '\u00ad' | '®' | '¯' | '°' | '±' | '²' | '³' | '´' | 'µ' | '¶' | '·' | '¸' | '¹' | 'º' | '»' | '¼' | '½' | '¾' | '¿' | 'À' | 'Á' | 'Â' | 'Ã' | 'Ä' | 'Å' | 'Æ' | 'Ç' | 'È' | 'É' | 'Ê' | 'Ë' | 'Ì' | 'Í' | 'Î' | 'Ï' | 'Ð' | 'Ñ' | 'Ò' | 'Ó' | 'Ô' | 'Õ' | 'Ö' | '×' | 'Ø' | 'Ù' | 'Ú' | 'Û' | 'Ü' | 'Ý' | 'Þ' | 'ß' | 'à' | 'á' | 'â' | 'ã' | 'ä' | 'å' | 'æ' | 'ç' | 'è' | 'é' | 'ê' | 'ë' | 'ì' | 'í' | 'î' | 'ï' | 'ð' | 'ñ' | 'ò' | 'ó' | 'ô' | 'õ' | 'ö' | '÷' | 'ø' | 'ù' | 'ú' | 'û' | 'ü' | 'ý' | 'þ' | 'ÿ') [(-\U0010ffff]))> */ + nil, + /* 34 mlLiteralString <- <('\'' '\'' '\'' <mlLiteralBody> ('\'' '\'' '\'') Action21)> */ + nil, + /* 35 mlLiteralBody <- <(!('\'' '\'' '\'') (mlLiteralChar / newline))*> */ + nil, + /* 36 mlLiteralChar <- <('\t' / [ -\U0010ffff])> */ + nil, + /* 37 hexdigit <- <((&('a' | 'b' | 'c' | 'd' | 'e' | 'f') [a-f]) | (&('A' | 'B' | 'C' | 'D' | 'E' | 'F') [A-F]) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') [0-9]))> */ + func() bool { + position267, tokenIndex267 := position, tokenIndex + { + position268 := position + { + switch buffer[position] { + case 'a', 'b', 'c', 'd', 'e', 'f': + if c := buffer[position]; c < rune('a') || c > rune('f') { + goto l267 + } + position++ + break + case 'A', 'B', 'C', 'D', 'E', 'F': + if c := buffer[position]; c < rune('A') || c > rune('F') { + goto l267 + } + position++ + break + default: + if c := buffer[position]; c < rune('0') || c > rune('9') { + goto l267 + } + position++ + break + } + } + + add(rulehexdigit, position268) + } + return true + l267: + position, tokenIndex = position267, tokenIndex267 + return false + }, + /* 38 hexQuad <- <(hexdigit hexdigit hexdigit hexdigit)> */ + func() bool { + position270, tokenIndex270 := position, tokenIndex + { + position271 := position + if !_rules[rulehexdigit]() { + goto l270 + } + if !_rules[rulehexdigit]() { + goto l270 + } + if !_rules[rulehexdigit]() { + goto l270 + } + if !_rules[rulehexdigit]() { + goto l270 + } + add(rulehexQuad, position271) + } + return true + l270: + position, tokenIndex = position270, tokenIndex270 + return false + }, + /* 39 boolean <- <(('t' 'r' 'u' 'e') / ('f' 'a' 'l' 's' 'e'))> */ + nil, + /* 40 dateFullYear <- <digitQuad> */ + nil, + /* 41 dateMonth <- <digitDual> */ + nil, + /* 42 dateMDay <- <digitDual> */ + nil, + /* 43 timeHour <- <digitDual> */ + func() bool { + position276, tokenIndex276 := position, tokenIndex + { + position277 := position + if !_rules[ruledigitDual]() { + goto l276 + } + add(ruletimeHour, position277) + } + return true + l276: + position, tokenIndex = position276, tokenIndex276 + return false + }, + /* 44 timeMinute <- <digitDual> */ + func() bool { + position278, tokenIndex278 := position, tokenIndex + { + position279 := position + if !_rules[ruledigitDual]() { + goto l278 + } + add(ruletimeMinute, position279) + } + return true + l278: + position, tokenIndex = position278, tokenIndex278 + return false + }, + /* 45 timeSecond <- <digitDual> */ + nil, + /* 46 timeSecfrac <- <('.' digit+)> */ + nil, + /* 47 timeNumoffset <- <(('-' / '+') timeHour ':' timeMinute)> */ + nil, + /* 48 timeOffset <- <('Z' / timeNumoffset)> */ + nil, + /* 49 partialTime <- <(timeHour ':' timeMinute ':' timeSecond timeSecfrac?)> */ + func() bool { + position284, tokenIndex284 := position, tokenIndex + { + position285 := position + if !_rules[ruletimeHour]() { + goto l284 + } + if buffer[position] != rune(':') { + goto l284 + } + position++ + if !_rules[ruletimeMinute]() { + goto l284 + } + if buffer[position] != rune(':') { + goto l284 + } + position++ + { + position286 := position + if !_rules[ruledigitDual]() { + goto l284 + } + add(ruletimeSecond, position286) + } + { + position287, tokenIndex287 := position, tokenIndex + { + position289 := position + if buffer[position] != rune('.') { + goto l287 + } + position++ + if !_rules[ruledigit]() { + goto l287 + } + l290: + { + position291, tokenIndex291 := position, tokenIndex + if !_rules[ruledigit]() { + goto l291 + } + goto l290 + l291: + position, tokenIndex = position291, tokenIndex291 + } + add(ruletimeSecfrac, position289) + } + goto l288 + l287: + position, tokenIndex = position287, tokenIndex287 + } + l288: + add(rulepartialTime, position285) + } + return true + l284: + position, tokenIndex = position284, tokenIndex284 + return false + }, + /* 50 fullDate <- <(dateFullYear '-' dateMonth '-' dateMDay)> */ + nil, + /* 51 fullTime <- <(partialTime timeOffset)> */ + nil, + /* 52 datetime <- <((fullDate ('T' fullTime)?) / partialTime)> */ + nil, + /* 53 digit <- <[0-9]> */ + func() bool { + position295, tokenIndex295 := position, tokenIndex + { + position296 := position + if c := buffer[position]; c < rune('0') || c > rune('9') { + goto l295 + } + position++ + add(ruledigit, position296) + } + return true + l295: + position, tokenIndex = position295, tokenIndex295 + return false + }, + /* 54 digitDual <- <(digit digit)> */ + func() bool { + position297, tokenIndex297 := position, tokenIndex + { + position298 := position + if !_rules[ruledigit]() { + goto l297 + } + if !_rules[ruledigit]() { + goto l297 + } + add(ruledigitDual, position298) + } + return true + l297: + position, tokenIndex = position297, tokenIndex297 + return false + }, + /* 55 digitQuad <- <(digitDual digitDual)> */ + nil, + /* 56 array <- <('[' Action22 wsnl arrayValues? wsnl ']')> */ + nil, + /* 57 arrayValues <- <(val Action23 (wsnl comment? wsnl arraySep wsnl comment? wsnl val Action24)* wsnl arraySep? wsnl comment?)> */ + nil, + /* 58 arraySep <- <','> */ + func() bool { + position302, tokenIndex302 := position, tokenIndex + { + position303 := position + if buffer[position] != rune(',') { + goto l302 + } + position++ + add(rulearraySep, position303) + } + return true + l302: + position, tokenIndex = position302, tokenIndex302 + return false + }, + /* 60 Action0 <- <{ _ = buffer }> */ + nil, + nil, + /* 62 Action1 <- <{ p.SetTableString(begin, end) }> */ + nil, + /* 63 Action2 <- <{ p.AddLineCount(end - begin) }> */ + nil, + /* 64 Action3 <- <{ p.AddLineCount(end - begin) }> */ + nil, + /* 65 Action4 <- <{ p.AddKeyValue() }> */ + nil, + /* 66 Action5 <- <{ p.SetKey(p.buffer, begin, end) }> */ + nil, + /* 67 Action6 <- <{ p.SetKey(p.buffer, begin-1, end+1) }> */ + nil, + /* 68 Action7 <- <{ p.SetTime(begin, end) }> */ + nil, + /* 69 Action8 <- <{ p.SetFloat64(begin, end) }> */ + nil, + /* 70 Action9 <- <{ p.SetInt64(begin, end) }> */ + nil, + /* 71 Action10 <- <{ p.SetString(begin, end) }> */ + nil, + /* 72 Action11 <- <{ p.SetBool(begin, end) }> */ + nil, + /* 73 Action12 <- <{ p.SetArray(begin, end) }> */ + nil, + /* 74 Action13 <- <{ p.SetTable(p.buffer, begin, end) }> */ + nil, + /* 75 Action14 <- <{ p.SetArrayTable(p.buffer, begin, end) }> */ + nil, + /* 76 Action15 <- <{ p.StartInlineTable() }> */ + nil, + /* 77 Action16 <- <{ p.EndInlineTable() }> */ + nil, + /* 78 Action17 <- <{ p.SetBasicString(p.buffer, begin, end) }> */ + nil, + /* 79 Action18 <- <{ p.SetMultilineString() }> */ + nil, + /* 80 Action19 <- <{ p.AddMultilineBasicBody(p.buffer, begin, end) }> */ + nil, + /* 81 Action20 <- <{ p.SetLiteralString(p.buffer, begin, end) }> */ + nil, + /* 82 Action21 <- <{ p.SetMultilineLiteralString(p.buffer, begin, end) }> */ + nil, + /* 83 Action22 <- <{ p.StartArray() }> */ + nil, + /* 84 Action23 <- <{ p.AddArrayVal() }> */ + nil, + /* 85 Action24 <- <{ p.AddArrayVal() }> */ + nil, + } + p.rules = _rules +} diff --git a/vendor/github.com/naoina/toml/util.go b/vendor/github.com/naoina/toml/util.go new file mode 100644 index 000000000..f882f4e5f --- /dev/null +++ b/vendor/github.com/naoina/toml/util.go @@ -0,0 +1,65 @@ +package toml + +import ( + "fmt" + "reflect" + "strings" +) + +const fieldTagName = "toml" + +// fieldCache maps normalized field names to their position in a struct. +type fieldCache struct { + named map[string]fieldInfo // fields with an explicit name in tag + auto map[string]fieldInfo // fields with auto-assigned normalized names +} + +type fieldInfo struct { + index []int + name string + ignored bool +} + +func makeFieldCache(cfg *Config, rt reflect.Type) fieldCache { + named, auto := make(map[string]fieldInfo), make(map[string]fieldInfo) + for i := 0; i < rt.NumField(); i++ { + ft := rt.Field(i) + // skip unexported fields + if ft.PkgPath != "" && !ft.Anonymous { + continue + } + col, _ := extractTag(ft.Tag.Get(fieldTagName)) + info := fieldInfo{index: ft.Index, name: ft.Name, ignored: col == "-"} + if col == "" || col == "-" { + auto[cfg.NormFieldName(rt, ft.Name)] = info + } else { + named[col] = info + } + } + return fieldCache{named, auto} +} + +func (fc fieldCache) findField(cfg *Config, rv reflect.Value, name string) (reflect.Value, string, error) { + info, found := fc.named[name] + if !found { + info, found = fc.auto[cfg.NormFieldName(rv.Type(), name)] + } + if !found { + if cfg.MissingField == nil { + return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' is not defined in %v", name, rv.Type()) + } else { + return reflect.Value{}, "", cfg.MissingField(rv.Type(), name) + } + } else if info.ignored { + return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' in %v cannot be set through TOML", name, rv.Type()) + } + return rv.FieldByIndex(info.index), info.name, nil +} + +func extractTag(tag string) (col, rest string) { + tags := strings.SplitN(tag, ",", 2) + if len(tags) == 2 { + return strings.TrimSpace(tags[0]), strings.TrimSpace(tags[1]) + } + return strings.TrimSpace(tags[0]), "" +} diff --git a/vendor/github.com/olekukonko/tablewriter/LICENCE.md b/vendor/github.com/olekukonko/tablewriter/LICENCE.md new file mode 100644 index 000000000..1fd848425 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/LICENCE.md @@ -0,0 +1,19 @@ +Copyright (C) 2014 by Oleku Konko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
\ No newline at end of file diff --git a/vendor/github.com/olekukonko/tablewriter/README.md b/vendor/github.com/olekukonko/tablewriter/README.md new file mode 100644 index 000000000..805330adc --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/README.md @@ -0,0 +1,204 @@ +ASCII Table Writer +========= + +[](https://travis-ci.org/olekukonko/tablewriter) [](https://sourcegraph.com/github.com/olekukonko/tablewriter) + +Generate ASCII table on the fly ... Installation is simple as + + go get github.com/olekukonko/tablewriter + + +#### Features +- Automatic Padding +- Support Multiple Lines +- Supports Alignment +- Support Custom Separators +- Automatic Alignment of numbers & percentage +- Write directly to http , file etc via `io.Writer` +- Read directly from CSV file +- Optional row line via `SetRowLine` +- Normalise table header +- Make CSV Headers optional +- Enable or disable table border +- Set custom footer support +- Optional identical cells merging + + +#### Example 1 - Basic +```go +data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"B", "The Very very Bad Man", "288"}, + []string{"C", "The Ugly", "120"}, + []string{"D", "The Gopher", "800"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Name", "Sign", "Rating"}) + +for _, v := range data { + table.Append(v) +} +table.Render() // Send output +``` + +##### Output 1 +``` ++------+-----------------------+--------+ +| NAME | SIGN | RATING | ++------+-----------------------+--------+ +| A | The Good | 500 | +| B | The Very very Bad Man | 288 | +| C | The Ugly | 120 | +| D | The Gopher | 800 | ++------+-----------------------+--------+ +``` + +#### Example 2 - Without Border / Footer / Bulk Append +```go +data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) +table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer +table.SetBorder(false) // Set Border to false +table.AppendBulk(data) // Add Bulk Data +table.Render() +``` + +##### Output 2 +``` + + DATE | DESCRIPTION | CV2 | AMOUNT ++----------+--------------------------+-------+---------+ + 1/1/2014 | Domain name | 2233 | $10.98 + 1/1/2014 | January Hosting | 2233 | $54.95 + 1/4/2014 | February Hosting | 2233 | $51.00 + 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 ++----------+--------------------------+-------+---------+ + TOTAL | $146 93 + +-------+---------+ + +``` + + +#### Example 3 - CSV +```go +table, _ := tablewriter.NewCSV(os.Stdout, "test_info.csv", true) +table.SetAlignment(tablewriter.ALIGN_LEFT) // Set Alignment +table.Render() +``` + +##### Output 3 +``` ++----------+--------------+------+-----+---------+----------------+ +| FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA | ++----------+--------------+------+-----+---------+----------------+ +| user_id | smallint(5) | NO | PRI | NULL | auto_increment | +| username | varchar(10) | NO | | NULL | | +| password | varchar(100) | NO | | NULL | | ++----------+--------------+------+-----+---------+----------------+ +``` + +#### Example 4 - Custom Separator +```go +table, _ := tablewriter.NewCSV(os.Stdout, "test.csv", true) +table.SetRowLine(true) // Enable row line + +// Change table lines +table.SetCenterSeparator("*") +table.SetColumnSeparator("‡") +table.SetRowSeparator("-") + +table.SetAlignment(tablewriter.ALIGN_LEFT) +table.Render() +``` + +##### Output 4 +``` +*------------*-----------*---------* +╪ FIRST NAME ╪ LAST NAME ╪ SSN ╪ +*------------*-----------*---------* +╪ John ╪ Barry ╪ 123456 ╪ +*------------*-----------*---------* +╪ Kathy ╪ Smith ╪ 687987 ╪ +*------------*-----------*---------* +╪ Bob ╪ McCornick ╪ 3979870 ╪ +*------------*-----------*---------* +``` + +##### Example 5 - Markdown Format +```go +data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) +table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) +table.SetCenterSeparator("|") +table.AppendBulk(data) // Add Bulk Data +table.Render() +``` + +##### Output 5 +``` +| DATE | DESCRIPTION | CV2 | AMOUNT | +|----------|--------------------------|------|--------| +| 1/1/2014 | Domain name | 2233 | $10.98 | +| 1/1/2014 | January Hosting | 2233 | $54.95 | +| 1/4/2014 | February Hosting | 2233 | $51.00 | +| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 | +``` + +#### Example 6 - Identical cells merging +```go +data := [][]string{ + []string{"1/1/2014", "Domain name", "1234", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2345", "$54.95"}, + []string{"1/4/2014", "February Hosting", "3456", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) +table.SetFooter([]string{"", "", "Total", "$146.93"}) +table.SetAutoMergeCells(true) +table.SetRowLine(true) +table.AppendBulk(data) +table.Render() +``` + +##### Output 6 +``` ++----------+--------------------------+-------+---------+ +| DATE | DESCRIPTION | CV2 | AMOUNT | ++----------+--------------------------+-------+---------+ +| 1/1/2014 | Domain name | 1234 | $10.98 | ++ +--------------------------+-------+---------+ +| | January Hosting | 2345 | $54.95 | ++----------+--------------------------+-------+---------+ +| 1/4/2014 | February Hosting | 3456 | $51.00 | ++ +--------------------------+-------+---------+ +| | February Extra Bandwidth | 4567 | $30.00 | ++----------+--------------------------+-------+---------+ +| TOTAL | $146 93 | ++----------+--------------------------+-------+---------+ +``` + +#### TODO +- ~~Import Directly from CSV~~ - `done` +- ~~Support for `SetFooter`~~ - `done` +- ~~Support for `SetBorder`~~ - `done` +- ~~Support table with uneven rows~~ - `done` +- Support custom alignment +- General Improvement & Optimisation +- `NewHTML` Parse table from HTML diff --git a/vendor/github.com/olekukonko/tablewriter/csv.go b/vendor/github.com/olekukonko/tablewriter/csv.go new file mode 100644 index 000000000..98878303b --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/csv.go @@ -0,0 +1,52 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +package tablewriter + +import ( + "encoding/csv" + "io" + "os" +) + +// Start A new table by importing from a CSV file +// Takes io.Writer and csv File name +func NewCSV(writer io.Writer, fileName string, hasHeader bool) (*Table, error) { + file, err := os.Open(fileName) + if err != nil { + return &Table{}, err + } + defer file.Close() + csvReader := csv.NewReader(file) + t, err := NewCSVReader(writer, csvReader, hasHeader) + return t, err +} + +// Start a New Table Writer with csv.Reader +// This enables customisation such as reader.Comma = ';' +// See http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94 +func NewCSVReader(writer io.Writer, csvReader *csv.Reader, hasHeader bool) (*Table, error) { + t := NewWriter(writer) + if hasHeader { + // Read the first row + headers, err := csvReader.Read() + if err != nil { + return &Table{}, err + } + t.SetHeader(headers) + } + for { + record, err := csvReader.Read() + if err == io.EOF { + break + } else if err != nil { + return &Table{}, err + } + t.Append(record) + } + return t, nil +} diff --git a/vendor/github.com/olekukonko/tablewriter/table.go b/vendor/github.com/olekukonko/tablewriter/table.go new file mode 100644 index 000000000..3314bfba5 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/table.go @@ -0,0 +1,662 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +// Create & Generate text based table +package tablewriter + +import ( + "bytes" + "fmt" + "io" + "regexp" + "strings" +) + +const ( + MAX_ROW_WIDTH = 30 +) + +const ( + CENTER = "+" + ROW = "-" + COLUMN = "|" + SPACE = " " + NEWLINE = "\n" +) + +const ( + ALIGN_DEFAULT = iota + ALIGN_CENTER + ALIGN_RIGHT + ALIGN_LEFT +) + +var ( + decimal = regexp.MustCompile(`^-*\d*\.?\d*$`) + percent = regexp.MustCompile(`^-*\d*\.?\d*$%$`) +) + +type Border struct { + Left bool + Right bool + Top bool + Bottom bool +} + +type Table struct { + out io.Writer + rows [][]string + lines [][][]string + cs map[int]int + rs map[int]int + headers []string + footers []string + autoFmt bool + autoWrap bool + mW int + pCenter string + pRow string + pColumn string + tColumn int + tRow int + hAlign int + fAlign int + align int + newLine string + rowLine bool + autoMergeCells bool + hdrLine bool + borders Border + colSize int +} + +// Start New Table +// Take io.Writer Directly +func NewWriter(writer io.Writer) *Table { + t := &Table{ + out: writer, + rows: [][]string{}, + lines: [][][]string{}, + cs: make(map[int]int), + rs: make(map[int]int), + headers: []string{}, + footers: []string{}, + autoFmt: true, + autoWrap: true, + mW: MAX_ROW_WIDTH, + pCenter: CENTER, + pRow: ROW, + pColumn: COLUMN, + tColumn: -1, + tRow: -1, + hAlign: ALIGN_DEFAULT, + fAlign: ALIGN_DEFAULT, + align: ALIGN_DEFAULT, + newLine: NEWLINE, + rowLine: false, + hdrLine: true, + borders: Border{Left: true, Right: true, Bottom: true, Top: true}, + colSize: -1} + return t +} + +// Render table output +func (t Table) Render() { + if t.borders.Top { + t.printLine(true) + } + t.printHeading() + if t.autoMergeCells { + t.printRowsMergeCells() + } else { + t.printRows() + } + + if !t.rowLine && t.borders.Bottom { + t.printLine(true) + } + t.printFooter() + +} + +// Set table header +func (t *Table) SetHeader(keys []string) { + t.colSize = len(keys) + for i, v := range keys { + t.parseDimension(v, i, -1) + t.headers = append(t.headers, v) + } +} + +// Set table Footer +func (t *Table) SetFooter(keys []string) { + //t.colSize = len(keys) + for i, v := range keys { + t.parseDimension(v, i, -1) + t.footers = append(t.footers, v) + } +} + +// Turn header autoformatting on/off. Default is on (true). +func (t *Table) SetAutoFormatHeaders(auto bool) { + t.autoFmt = auto +} + +// Turn automatic multiline text adjustment on/off. Default is on (true). +func (t *Table) SetAutoWrapText(auto bool) { + t.autoWrap = auto +} + +// Set the Default column width +func (t *Table) SetColWidth(width int) { + t.mW = width +} + +// Set the Column Separator +func (t *Table) SetColumnSeparator(sep string) { + t.pColumn = sep +} + +// Set the Row Separator +func (t *Table) SetRowSeparator(sep string) { + t.pRow = sep +} + +// Set the center Separator +func (t *Table) SetCenterSeparator(sep string) { + t.pCenter = sep +} + +// Set Header Alignment +func (t *Table) SetHeaderAlignment(hAlign int) { + t.hAlign = hAlign +} + +// Set Footer Alignment +func (t *Table) SetFooterAlignment(fAlign int) { + t.fAlign = fAlign +} + +// Set Table Alignment +func (t *Table) SetAlignment(align int) { + t.align = align +} + +// Set New Line +func (t *Table) SetNewLine(nl string) { + t.newLine = nl +} + +// Set Header Line +// This would enable / disable a line after the header +func (t *Table) SetHeaderLine(line bool) { + t.hdrLine = line +} + +// Set Row Line +// This would enable / disable a line on each row of the table +func (t *Table) SetRowLine(line bool) { + t.rowLine = line +} + +// Set Auto Merge Cells +// This would enable / disable the merge of cells with identical values +func (t *Table) SetAutoMergeCells(auto bool) { + t.autoMergeCells = auto +} + +// Set Table Border +// This would enable / disable line around the table +func (t *Table) SetBorder(border bool) { + t.SetBorders(Border{border, border, border, border}) +} + +func (t *Table) SetBorders(border Border) { + t.borders = border +} + +// Append row to table +func (t *Table) Append(row []string) { + rowSize := len(t.headers) + if rowSize > t.colSize { + t.colSize = rowSize + } + + n := len(t.lines) + line := [][]string{} + for i, v := range row { + + // Detect string width + // Detect String height + // Break strings into words + out := t.parseDimension(v, i, n) + + // Append broken words + line = append(line, out) + } + t.lines = append(t.lines, line) +} + +// Allow Support for Bulk Append +// Eliminates repeated for loops +func (t *Table) AppendBulk(rows [][]string) { + for _, row := range rows { + t.Append(row) + } +} + +// Print line based on row width +func (t Table) printLine(nl bool) { + fmt.Fprint(t.out, t.pCenter) + for i := 0; i < len(t.cs); i++ { + v := t.cs[i] + fmt.Fprintf(t.out, "%s%s%s%s", + t.pRow, + strings.Repeat(string(t.pRow), v), + t.pRow, + t.pCenter) + } + if nl { + fmt.Fprint(t.out, t.newLine) + } +} + +// Print line based on row width with our without cell separator +func (t Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) { + fmt.Fprint(t.out, t.pCenter) + for i := 0; i < len(t.cs); i++ { + v := t.cs[i] + if i > len(displayCellSeparator) || displayCellSeparator[i] { + // Display the cell separator + fmt.Fprintf(t.out, "%s%s%s%s", + t.pRow, + strings.Repeat(string(t.pRow), v), + t.pRow, + t.pCenter) + } else { + // Don't display the cell separator for this cell + fmt.Fprintf(t.out, "%s%s", + strings.Repeat(" ", v+2), + t.pCenter) + } + } + if nl { + fmt.Fprint(t.out, t.newLine) + } +} + +// Return the PadRight function if align is left, PadLeft if align is right, +// and Pad by default +func pad(align int) func(string, string, int) string { + padFunc := Pad + switch align { + case ALIGN_LEFT: + padFunc = PadRight + case ALIGN_RIGHT: + padFunc = PadLeft + } + return padFunc +} + +// Print heading information +func (t Table) printHeading() { + // Check if headers is available + if len(t.headers) < 1 { + return + } + + // Check if border is set + // Replace with space if not set + fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE)) + + // Identify last column + end := len(t.cs) - 1 + + // Get pad function + padFunc := pad(t.hAlign) + + // Print Heading column + for i := 0; i <= end; i++ { + v := t.cs[i] + h := t.headers[i] + if t.autoFmt { + h = Title(h) + } + pad := ConditionString((i == end && !t.borders.Left), SPACE, t.pColumn) + fmt.Fprintf(t.out, " %s %s", + padFunc(h, SPACE, v), + pad) + } + // Next line + fmt.Fprint(t.out, t.newLine) + if t.hdrLine { + t.printLine(true) + } +} + +// Print heading information +func (t Table) printFooter() { + // Check if headers is available + if len(t.footers) < 1 { + return + } + + // Only print line if border is not set + if !t.borders.Bottom { + t.printLine(true) + } + // Check if border is set + // Replace with space if not set + fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE)) + + // Identify last column + end := len(t.cs) - 1 + + // Get pad function + padFunc := pad(t.fAlign) + + // Print Heading column + for i := 0; i <= end; i++ { + v := t.cs[i] + f := t.footers[i] + if t.autoFmt { + f = Title(f) + } + pad := ConditionString((i == end && !t.borders.Top), SPACE, t.pColumn) + + if len(t.footers[i]) == 0 { + pad = SPACE + } + fmt.Fprintf(t.out, " %s %s", + padFunc(f, SPACE, v), + pad) + } + // Next line + fmt.Fprint(t.out, t.newLine) + //t.printLine(true) + + hasPrinted := false + + for i := 0; i <= end; i++ { + v := t.cs[i] + pad := t.pRow + center := t.pCenter + length := len(t.footers[i]) + + if length > 0 { + hasPrinted = true + } + + // Set center to be space if length is 0 + if length == 0 && !t.borders.Right { + center = SPACE + } + + // Print first junction + if i == 0 { + fmt.Fprint(t.out, center) + } + + // Pad With space of length is 0 + if length == 0 { + pad = SPACE + } + // Ignore left space of it has printed before + if hasPrinted || t.borders.Left { + pad = t.pRow + center = t.pCenter + } + + // Change Center start position + if center == SPACE { + if i < end && len(t.footers[i+1]) != 0 { + center = t.pCenter + } + } + + // Print the footer + fmt.Fprintf(t.out, "%s%s%s%s", + pad, + strings.Repeat(string(pad), v), + pad, + center) + + } + + fmt.Fprint(t.out, t.newLine) + +} + +func (t Table) printRows() { + for i, lines := range t.lines { + t.printRow(lines, i) + } + +} + +// Print Row Information +// Adjust column alignment based on type + +func (t Table) printRow(columns [][]string, colKey int) { + // Get Maximum Height + max := t.rs[colKey] + total := len(columns) + + // TODO Fix uneven col size + // if total < t.colSize { + // for n := t.colSize - total; n < t.colSize ; n++ { + // columns = append(columns, []string{SPACE}) + // t.cs[n] = t.mW + // } + //} + + // Pad Each Height + // pads := []int{} + pads := []int{} + + for i, line := range columns { + length := len(line) + pad := max - length + pads = append(pads, pad) + for n := 0; n < pad; n++ { + columns[i] = append(columns[i], " ") + } + } + //fmt.Println(max, "\n") + for x := 0; x < max; x++ { + for y := 0; y < total; y++ { + + // Check if border is set + fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn)) + + fmt.Fprintf(t.out, SPACE) + str := columns[y][x] + + // This would print alignment + // Default alignment would use multiple configuration + switch t.align { + case ALIGN_CENTER: // + fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y])) + case ALIGN_RIGHT: + fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y])) + case ALIGN_LEFT: + fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y])) + default: + if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) { + fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y])) + } else { + fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y])) + + // TODO Custom alignment per column + //if max == 1 || pads[y] > 0 { + // fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y])) + //} else { + // fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y])) + //} + + } + } + fmt.Fprintf(t.out, SPACE) + } + // Check if border is set + // Replace with space if not set + fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE)) + fmt.Fprint(t.out, t.newLine) + } + + if t.rowLine { + t.printLine(true) + } +} + +// Print the rows of the table and merge the cells that are identical +func (t Table) printRowsMergeCells() { + var previousLine []string + var displayCellBorder []bool + var tmpWriter bytes.Buffer + for i, lines := range t.lines { + // We store the display of the current line in a tmp writer, as we need to know which border needs to be print above + previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine) + if i > 0 { //We don't need to print borders above first line + if t.rowLine { + t.printLineOptionalCellSeparators(true, displayCellBorder) + } + } + tmpWriter.WriteTo(t.out) + } + //Print the end of the table + if t.rowLine { + t.printLine(true) + } +} + +// Print Row Information to a writer and merge identical cells. +// Adjust column alignment based on type + +func (t Table) printRowMergeCells(writer io.Writer, columns [][]string, colKey int, previousLine []string) ([]string, []bool) { + // Get Maximum Height + max := t.rs[colKey] + total := len(columns) + + // Pad Each Height + pads := []int{} + + for i, line := range columns { + length := len(line) + pad := max - length + pads = append(pads, pad) + for n := 0; n < pad; n++ { + columns[i] = append(columns[i], " ") + } + } + + var displayCellBorder []bool + for x := 0; x < max; x++ { + for y := 0; y < total; y++ { + + // Check if border is set + fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn)) + + fmt.Fprintf(writer, SPACE) + + str := columns[y][x] + + if t.autoMergeCells { + //Store the full line to merge mutli-lines cells + fullLine := strings.Join(columns[y], " ") + if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" { + // If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty. + displayCellBorder = append(displayCellBorder, false) + str = "" + } else { + // First line or different content, keep the content and print the cell border + displayCellBorder = append(displayCellBorder, true) + } + } + + // This would print alignment + // Default alignment would use multiple configuration + switch t.align { + case ALIGN_CENTER: // + fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y])) + case ALIGN_RIGHT: + fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y])) + case ALIGN_LEFT: + fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y])) + default: + if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) { + fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y])) + } else { + fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y])) + } + } + fmt.Fprintf(writer, SPACE) + } + // Check if border is set + // Replace with space if not set + fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE)) + fmt.Fprint(writer, t.newLine) + } + + //The new previous line is the current one + previousLine = make([]string, total) + for y := 0; y < total; y++ { + previousLine[y] = strings.Join(columns[y], " ") //Store the full line for multi-lines cells + } + //Returns the newly added line and wether or not a border should be displayed above. + return previousLine, displayCellBorder +} + +func (t *Table) parseDimension(str string, colKey, rowKey int) []string { + var ( + raw []string + max int + ) + w := DisplayWidth(str) + // Calculate Width + // Check if with is grater than maximum width + if w > t.mW { + w = t.mW + } + + // Check if width exists + v, ok := t.cs[colKey] + if !ok || v < w || v == 0 { + t.cs[colKey] = w + } + + if rowKey == -1 { + return raw + } + // Calculate Height + if t.autoWrap { + raw, _ = WrapString(str, t.cs[colKey]) + } else { + raw = getLines(str) + } + + for _, line := range raw { + if w := DisplayWidth(line); w > max { + max = w + } + } + + // Make sure the with is the same length as maximum word + // Important for cases where the width is smaller than maxu word + if max > t.cs[colKey] { + t.cs[colKey] = max + } + + h := len(raw) + v, ok = t.rs[rowKey] + + if !ok || v < h || v == 0 { + t.rs[rowKey] = h + } + //fmt.Printf("Raw %+v %d\n", raw, len(raw)) + return raw +} diff --git a/vendor/github.com/olekukonko/tablewriter/test.csv b/vendor/github.com/olekukonko/tablewriter/test.csv new file mode 100644 index 000000000..1609327e9 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/test.csv @@ -0,0 +1,4 @@ +first_name,last_name,ssn +John,Barry,123456 +Kathy,Smith,687987 +Bob,McCornick,3979870
\ No newline at end of file diff --git a/vendor/github.com/olekukonko/tablewriter/test_info.csv b/vendor/github.com/olekukonko/tablewriter/test_info.csv new file mode 100644 index 000000000..e4c40e983 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/test_info.csv @@ -0,0 +1,4 @@ +Field,Type,Null,Key,Default,Extra +user_id,smallint(5),NO,PRI,NULL,auto_increment +username,varchar(10),NO,,NULL, +password,varchar(100),NO,,NULL,
\ No newline at end of file diff --git a/vendor/github.com/olekukonko/tablewriter/util.go b/vendor/github.com/olekukonko/tablewriter/util.go new file mode 100644 index 000000000..2deefbc52 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/util.go @@ -0,0 +1,72 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +package tablewriter + +import ( + "math" + "regexp" + "strings" + + "github.com/mattn/go-runewidth" +) + +var ansi = regexp.MustCompile("\033\\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]") + +func DisplayWidth(str string) int { + return runewidth.StringWidth(ansi.ReplaceAllLiteralString(str, "")) +} + +// Simple Condition for string +// Returns value based on condition +func ConditionString(cond bool, valid, inValid string) string { + if cond { + return valid + } + return inValid +} + +// Format Table Header +// Replace _ , . and spaces +func Title(name string) string { + name = strings.Replace(name, "_", " ", -1) + name = strings.Replace(name, ".", " ", -1) + name = strings.TrimSpace(name) + return strings.ToUpper(name) +} + +// Pad String +// Attempts to play string in the center +func Pad(s, pad string, width int) string { + gap := width - DisplayWidth(s) + if gap > 0 { + gapLeft := int(math.Ceil(float64(gap / 2))) + gapRight := gap - gapLeft + return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight) + } + return s +} + +// Pad String Right position +// This would pace string at the left side fo the screen +func PadRight(s, pad string, width int) string { + gap := width - DisplayWidth(s) + if gap > 0 { + return s + strings.Repeat(string(pad), gap) + } + return s +} + +// Pad String Left position +// This would pace string at the right side fo the screen +func PadLeft(s, pad string, width int) string { + gap := width - DisplayWidth(s) + if gap > 0 { + return strings.Repeat(string(pad), gap) + s + } + return s +} diff --git a/vendor/github.com/olekukonko/tablewriter/wrap.go b/vendor/github.com/olekukonko/tablewriter/wrap.go new file mode 100644 index 000000000..5290fb65a --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/wrap.go @@ -0,0 +1,103 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +package tablewriter + +import ( + "math" + "strings" + "unicode/utf8" +) + +var ( + nl = "\n" + sp = " " +) + +const defaultPenalty = 1e5 + +// Wrap wraps s into a paragraph of lines of length lim, with minimal +// raggedness. +func WrapString(s string, lim int) ([]string, int) { + words := strings.Split(strings.Replace(s, nl, sp, -1), sp) + var lines []string + max := 0 + for _, v := range words { + max = len(v) + if max > lim { + lim = max + } + } + for _, line := range WrapWords(words, 1, lim, defaultPenalty) { + lines = append(lines, strings.Join(line, sp)) + } + return lines, lim +} + +// WrapWords is the low-level line-breaking algorithm, useful if you need more +// control over the details of the text wrapping process. For most uses, +// WrapString will be sufficient and more convenient. +// +// WrapWords splits a list of words into lines with minimal "raggedness", +// treating each rune as one unit, accounting for spc units between adjacent +// words on each line, and attempting to limit lines to lim units. Raggedness +// is the total error over all lines, where error is the square of the +// difference of the length of the line and lim. Too-long lines (which only +// happen when a single word is longer than lim units) have pen penalty units +// added to the error. +func WrapWords(words []string, spc, lim, pen int) [][]string { + n := len(words) + + length := make([][]int, n) + for i := 0; i < n; i++ { + length[i] = make([]int, n) + length[i][i] = utf8.RuneCountInString(words[i]) + for j := i + 1; j < n; j++ { + length[i][j] = length[i][j-1] + spc + utf8.RuneCountInString(words[j]) + } + } + nbrk := make([]int, n) + cost := make([]int, n) + for i := range cost { + cost[i] = math.MaxInt32 + } + for i := n - 1; i >= 0; i-- { + if length[i][n-1] <= lim { + cost[i] = 0 + nbrk[i] = n + } else { + for j := i + 1; j < n; j++ { + d := lim - length[i][j-1] + c := d*d + cost[j] + if length[i][j-1] > lim { + c += pen // too-long lines get a worse penalty + } + if c < cost[i] { + cost[i] = c + nbrk[i] = j + } + } + } + } + var lines [][]string + i := 0 + for i < n { + lines = append(lines, words[i:nbrk[i]]) + i = nbrk[i] + } + return lines +} + +// getLines decomposes a multiline string into a slice of strings. +func getLines(s string) []string { + var lines []string + + for _, line := range strings.Split(s, nl) { + lines = append(lines, line) + } + return lines +} diff --git a/vendor/golang.org/x/crypto/curve25519/const_amd64.h b/vendor/golang.org/x/crypto/curve25519/const_amd64.h new file mode 100644 index 000000000..80ad2220f --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/const_amd64.h @@ -0,0 +1,8 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +#define REDMASK51 0x0007FFFFFFFFFFFF diff --git a/vendor/golang.org/x/crypto/curve25519/const_amd64.s b/vendor/golang.org/x/crypto/curve25519/const_amd64.s new file mode 100644 index 000000000..0ad539885 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/const_amd64.s @@ -0,0 +1,20 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine + +// These constants cannot be encoded in non-MOVQ immediates. +// We access them directly from memory instead. + +DATA ·_121666_213(SB)/8, $996687872 +GLOBL ·_121666_213(SB), 8, $8 + +DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA +GLOBL ·_2P0(SB), 8, $8 + +DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE +GLOBL ·_2P1234(SB), 8, $8 diff --git a/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s b/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s new file mode 100644 index 000000000..45484d1b5 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/cswap_amd64.s @@ -0,0 +1,88 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine + +// func cswap(inout *[5]uint64, v uint64) +TEXT ·cswap(SB),7,$0 + MOVQ inout+0(FP),DI + MOVQ v+8(FP),SI + + CMPQ SI,$1 + MOVQ 0(DI),SI + MOVQ 80(DI),DX + MOVQ 8(DI),CX + MOVQ 88(DI),R8 + MOVQ SI,R9 + CMOVQEQ DX,SI + CMOVQEQ R9,DX + MOVQ CX,R9 + CMOVQEQ R8,CX + CMOVQEQ R9,R8 + MOVQ SI,0(DI) + MOVQ DX,80(DI) + MOVQ CX,8(DI) + MOVQ R8,88(DI) + MOVQ 16(DI),SI + MOVQ 96(DI),DX + MOVQ 24(DI),CX + MOVQ 104(DI),R8 + MOVQ SI,R9 + CMOVQEQ DX,SI + CMOVQEQ R9,DX + MOVQ CX,R9 + CMOVQEQ R8,CX + CMOVQEQ R9,R8 + MOVQ SI,16(DI) + MOVQ DX,96(DI) + MOVQ CX,24(DI) + MOVQ R8,104(DI) + MOVQ 32(DI),SI + MOVQ 112(DI),DX + MOVQ 40(DI),CX + MOVQ 120(DI),R8 + MOVQ SI,R9 + CMOVQEQ DX,SI + CMOVQEQ R9,DX + MOVQ CX,R9 + CMOVQEQ R8,CX + CMOVQEQ R9,R8 + MOVQ SI,32(DI) + MOVQ DX,112(DI) + MOVQ CX,40(DI) + MOVQ R8,120(DI) + MOVQ 48(DI),SI + MOVQ 128(DI),DX + MOVQ 56(DI),CX + MOVQ 136(DI),R8 + MOVQ SI,R9 + CMOVQEQ DX,SI + CMOVQEQ R9,DX + MOVQ CX,R9 + CMOVQEQ R8,CX + CMOVQEQ R9,R8 + MOVQ SI,48(DI) + MOVQ DX,128(DI) + MOVQ CX,56(DI) + MOVQ R8,136(DI) + MOVQ 64(DI),SI + MOVQ 144(DI),DX + MOVQ 72(DI),CX + MOVQ 152(DI),R8 + MOVQ SI,R9 + CMOVQEQ DX,SI + CMOVQEQ R9,DX + MOVQ CX,R9 + CMOVQEQ R8,CX + CMOVQEQ R9,R8 + MOVQ SI,64(DI) + MOVQ DX,144(DI) + MOVQ CX,72(DI) + MOVQ R8,152(DI) + MOVQ DI,AX + MOVQ SI,DX + RET diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go new file mode 100644 index 000000000..6918c47fc --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go @@ -0,0 +1,841 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// We have a implementation in amd64 assembly so this code is only run on +// non-amd64 platforms. The amd64 assembly does not support gccgo. +// +build !amd64 gccgo appengine + +package curve25519 + +// This code is a port of the public domain, "ref10" implementation of +// curve25519 from SUPERCOP 20130419 by D. J. Bernstein. + +// fieldElement represents an element of the field GF(2^255 - 19). An element +// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 +// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on +// context. +type fieldElement [10]int32 + +func feZero(fe *fieldElement) { + for i := range fe { + fe[i] = 0 + } +} + +func feOne(fe *fieldElement) { + feZero(fe) + fe[0] = 1 +} + +func feAdd(dst, a, b *fieldElement) { + for i := range dst { + dst[i] = a[i] + b[i] + } +} + +func feSub(dst, a, b *fieldElement) { + for i := range dst { + dst[i] = a[i] - b[i] + } +} + +func feCopy(dst, src *fieldElement) { + for i := range dst { + dst[i] = src[i] + } +} + +// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0. +// +// Preconditions: b in {0,1}. +func feCSwap(f, g *fieldElement, b int32) { + var x fieldElement + b = -b + for i := range x { + x[i] = b & (f[i] ^ g[i]) + } + + for i := range f { + f[i] ^= x[i] + } + for i := range g { + g[i] ^= x[i] + } +} + +// load3 reads a 24-bit, little-endian value from in. +func load3(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + return r +} + +// load4 reads a 32-bit, little-endian value from in. +func load4(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + r |= int64(in[3]) << 24 + return r +} + +func feFromBytes(dst *fieldElement, src *[32]byte) { + h0 := load4(src[:]) + h1 := load3(src[4:]) << 6 + h2 := load3(src[7:]) << 5 + h3 := load3(src[10:]) << 3 + h4 := load3(src[13:]) << 2 + h5 := load4(src[16:]) + h6 := load3(src[20:]) << 7 + h7 := load3(src[23:]) << 5 + h8 := load3(src[26:]) << 4 + h9 := load3(src[29:]) << 2 + + var carry [10]int64 + carry[9] = (h9 + 1<<24) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + carry[1] = (h1 + 1<<24) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[3] = (h3 + 1<<24) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[5] = (h5 + 1<<24) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + carry[7] = (h7 + 1<<24) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[0] = (h0 + 1<<25) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[2] = (h2 + 1<<25) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[4] = (h4 + 1<<25) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[6] = (h6 + 1<<25) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + carry[8] = (h8 + 1<<25) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + dst[0] = int32(h0) + dst[1] = int32(h1) + dst[2] = int32(h2) + dst[3] = int32(h3) + dst[4] = int32(h4) + dst[5] = int32(h5) + dst[6] = int32(h6) + dst[7] = int32(h7) + dst[8] = int32(h8) + dst[9] = int32(h9) +} + +// feToBytes marshals h to s. +// Preconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Write p=2^255-19; q=floor(h/p). +// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). +// +// Proof: +// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. +// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. +// +// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). +// Then 0<y<1. +// +// Write r=h-pq. +// Have 0<=r<=p-1=2^255-20. +// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1. +// +// Write x=r+19(2^-255)r+y. +// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q. +// +// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1)) +// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. +func feToBytes(s *[32]byte, h *fieldElement) { + var carry [10]int32 + + q := (19*h[9] + (1 << 24)) >> 25 + q = (h[0] + q) >> 26 + q = (h[1] + q) >> 25 + q = (h[2] + q) >> 26 + q = (h[3] + q) >> 25 + q = (h[4] + q) >> 26 + q = (h[5] + q) >> 25 + q = (h[6] + q) >> 26 + q = (h[7] + q) >> 25 + q = (h[8] + q) >> 26 + q = (h[9] + q) >> 25 + + // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. + h[0] += 19 * q + // Goal: Output h-2^255 q, which is between 0 and 2^255-20. + + carry[0] = h[0] >> 26 + h[1] += carry[0] + h[0] -= carry[0] << 26 + carry[1] = h[1] >> 25 + h[2] += carry[1] + h[1] -= carry[1] << 25 + carry[2] = h[2] >> 26 + h[3] += carry[2] + h[2] -= carry[2] << 26 + carry[3] = h[3] >> 25 + h[4] += carry[3] + h[3] -= carry[3] << 25 + carry[4] = h[4] >> 26 + h[5] += carry[4] + h[4] -= carry[4] << 26 + carry[5] = h[5] >> 25 + h[6] += carry[5] + h[5] -= carry[5] << 25 + carry[6] = h[6] >> 26 + h[7] += carry[6] + h[6] -= carry[6] << 26 + carry[7] = h[7] >> 25 + h[8] += carry[7] + h[7] -= carry[7] << 25 + carry[8] = h[8] >> 26 + h[9] += carry[8] + h[8] -= carry[8] << 26 + carry[9] = h[9] >> 25 + h[9] -= carry[9] << 25 + // h10 = carry9 + + // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; + // evidently 2^255 h10-2^255 q = 0. + // Goal: Output h[0]+...+2^230 h[9]. + + s[0] = byte(h[0] >> 0) + s[1] = byte(h[0] >> 8) + s[2] = byte(h[0] >> 16) + s[3] = byte((h[0] >> 24) | (h[1] << 2)) + s[4] = byte(h[1] >> 6) + s[5] = byte(h[1] >> 14) + s[6] = byte((h[1] >> 22) | (h[2] << 3)) + s[7] = byte(h[2] >> 5) + s[8] = byte(h[2] >> 13) + s[9] = byte((h[2] >> 21) | (h[3] << 5)) + s[10] = byte(h[3] >> 3) + s[11] = byte(h[3] >> 11) + s[12] = byte((h[3] >> 19) | (h[4] << 6)) + s[13] = byte(h[4] >> 2) + s[14] = byte(h[4] >> 10) + s[15] = byte(h[4] >> 18) + s[16] = byte(h[5] >> 0) + s[17] = byte(h[5] >> 8) + s[18] = byte(h[5] >> 16) + s[19] = byte((h[5] >> 24) | (h[6] << 1)) + s[20] = byte(h[6] >> 7) + s[21] = byte(h[6] >> 15) + s[22] = byte((h[6] >> 23) | (h[7] << 3)) + s[23] = byte(h[7] >> 5) + s[24] = byte(h[7] >> 13) + s[25] = byte((h[7] >> 21) | (h[8] << 4)) + s[26] = byte(h[8] >> 4) + s[27] = byte(h[8] >> 12) + s[28] = byte((h[8] >> 20) | (h[9] << 6)) + s[29] = byte(h[9] >> 2) + s[30] = byte(h[9] >> 10) + s[31] = byte(h[9] >> 18) +} + +// feMul calculates h = f * g +// Can overlap h with f or g. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Notes on implementation strategy: +// +// Using schoolbook multiplication. +// Karatsuba would save a little in some cost models. +// +// Most multiplications by 2 and 19 are 32-bit precomputations; +// cheaper than 64-bit postcomputations. +// +// There is one remaining multiplication by 19 in the carry chain; +// one *19 precomputation can be merged into this, +// but the resulting data flow is considerably less clean. +// +// There are 12 carries below. +// 10 of them are 2-way parallelizable and vectorizable. +// Can get away with 11 carries, but then data flow is much deeper. +// +// With tighter constraints on inputs can squeeze carries into int32. +func feMul(h, f, g *fieldElement) { + f0 := f[0] + f1 := f[1] + f2 := f[2] + f3 := f[3] + f4 := f[4] + f5 := f[5] + f6 := f[6] + f7 := f[7] + f8 := f[8] + f9 := f[9] + g0 := g[0] + g1 := g[1] + g2 := g[2] + g3 := g[3] + g4 := g[4] + g5 := g[5] + g6 := g[6] + g7 := g[7] + g8 := g[8] + g9 := g[9] + g1_19 := 19 * g1 // 1.4*2^29 + g2_19 := 19 * g2 // 1.4*2^30; still ok + g3_19 := 19 * g3 + g4_19 := 19 * g4 + g5_19 := 19 * g5 + g6_19 := 19 * g6 + g7_19 := 19 * g7 + g8_19 := 19 * g8 + g9_19 := 19 * g9 + f1_2 := 2 * f1 + f3_2 := 2 * f3 + f5_2 := 2 * f5 + f7_2 := 2 * f7 + f9_2 := 2 * f9 + f0g0 := int64(f0) * int64(g0) + f0g1 := int64(f0) * int64(g1) + f0g2 := int64(f0) * int64(g2) + f0g3 := int64(f0) * int64(g3) + f0g4 := int64(f0) * int64(g4) + f0g5 := int64(f0) * int64(g5) + f0g6 := int64(f0) * int64(g6) + f0g7 := int64(f0) * int64(g7) + f0g8 := int64(f0) * int64(g8) + f0g9 := int64(f0) * int64(g9) + f1g0 := int64(f1) * int64(g0) + f1g1_2 := int64(f1_2) * int64(g1) + f1g2 := int64(f1) * int64(g2) + f1g3_2 := int64(f1_2) * int64(g3) + f1g4 := int64(f1) * int64(g4) + f1g5_2 := int64(f1_2) * int64(g5) + f1g6 := int64(f1) * int64(g6) + f1g7_2 := int64(f1_2) * int64(g7) + f1g8 := int64(f1) * int64(g8) + f1g9_38 := int64(f1_2) * int64(g9_19) + f2g0 := int64(f2) * int64(g0) + f2g1 := int64(f2) * int64(g1) + f2g2 := int64(f2) * int64(g2) + f2g3 := int64(f2) * int64(g3) + f2g4 := int64(f2) * int64(g4) + f2g5 := int64(f2) * int64(g5) + f2g6 := int64(f2) * int64(g6) + f2g7 := int64(f2) * int64(g7) + f2g8_19 := int64(f2) * int64(g8_19) + f2g9_19 := int64(f2) * int64(g9_19) + f3g0 := int64(f3) * int64(g0) + f3g1_2 := int64(f3_2) * int64(g1) + f3g2 := int64(f3) * int64(g2) + f3g3_2 := int64(f3_2) * int64(g3) + f3g4 := int64(f3) * int64(g4) + f3g5_2 := int64(f3_2) * int64(g5) + f3g6 := int64(f3) * int64(g6) + f3g7_38 := int64(f3_2) * int64(g7_19) + f3g8_19 := int64(f3) * int64(g8_19) + f3g9_38 := int64(f3_2) * int64(g9_19) + f4g0 := int64(f4) * int64(g0) + f4g1 := int64(f4) * int64(g1) + f4g2 := int64(f4) * int64(g2) + f4g3 := int64(f4) * int64(g3) + f4g4 := int64(f4) * int64(g4) + f4g5 := int64(f4) * int64(g5) + f4g6_19 := int64(f4) * int64(g6_19) + f4g7_19 := int64(f4) * int64(g7_19) + f4g8_19 := int64(f4) * int64(g8_19) + f4g9_19 := int64(f4) * int64(g9_19) + f5g0 := int64(f5) * int64(g0) + f5g1_2 := int64(f5_2) * int64(g1) + f5g2 := int64(f5) * int64(g2) + f5g3_2 := int64(f5_2) * int64(g3) + f5g4 := int64(f5) * int64(g4) + f5g5_38 := int64(f5_2) * int64(g5_19) + f5g6_19 := int64(f5) * int64(g6_19) + f5g7_38 := int64(f5_2) * int64(g7_19) + f5g8_19 := int64(f5) * int64(g8_19) + f5g9_38 := int64(f5_2) * int64(g9_19) + f6g0 := int64(f6) * int64(g0) + f6g1 := int64(f6) * int64(g1) + f6g2 := int64(f6) * int64(g2) + f6g3 := int64(f6) * int64(g3) + f6g4_19 := int64(f6) * int64(g4_19) + f6g5_19 := int64(f6) * int64(g5_19) + f6g6_19 := int64(f6) * int64(g6_19) + f6g7_19 := int64(f6) * int64(g7_19) + f6g8_19 := int64(f6) * int64(g8_19) + f6g9_19 := int64(f6) * int64(g9_19) + f7g0 := int64(f7) * int64(g0) + f7g1_2 := int64(f7_2) * int64(g1) + f7g2 := int64(f7) * int64(g2) + f7g3_38 := int64(f7_2) * int64(g3_19) + f7g4_19 := int64(f7) * int64(g4_19) + f7g5_38 := int64(f7_2) * int64(g5_19) + f7g6_19 := int64(f7) * int64(g6_19) + f7g7_38 := int64(f7_2) * int64(g7_19) + f7g8_19 := int64(f7) * int64(g8_19) + f7g9_38 := int64(f7_2) * int64(g9_19) + f8g0 := int64(f8) * int64(g0) + f8g1 := int64(f8) * int64(g1) + f8g2_19 := int64(f8) * int64(g2_19) + f8g3_19 := int64(f8) * int64(g3_19) + f8g4_19 := int64(f8) * int64(g4_19) + f8g5_19 := int64(f8) * int64(g5_19) + f8g6_19 := int64(f8) * int64(g6_19) + f8g7_19 := int64(f8) * int64(g7_19) + f8g8_19 := int64(f8) * int64(g8_19) + f8g9_19 := int64(f8) * int64(g9_19) + f9g0 := int64(f9) * int64(g0) + f9g1_38 := int64(f9_2) * int64(g1_19) + f9g2_19 := int64(f9) * int64(g2_19) + f9g3_38 := int64(f9_2) * int64(g3_19) + f9g4_19 := int64(f9) * int64(g4_19) + f9g5_38 := int64(f9_2) * int64(g5_19) + f9g6_19 := int64(f9) * int64(g6_19) + f9g7_38 := int64(f9_2) * int64(g7_19) + f9g8_19 := int64(f9) * int64(g8_19) + f9g9_38 := int64(f9_2) * int64(g9_19) + h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38 + h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19 + h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38 + h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19 + h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38 + h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19 + h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38 + h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19 + h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38 + h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 + var carry [10]int64 + + // |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) + // i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 + // |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) + // i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + // |h0| <= 2^25 + // |h4| <= 2^25 + // |h1| <= 1.51*2^58 + // |h5| <= 1.51*2^58 + + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + // |h1| <= 2^24; from now on fits into int32 + // |h5| <= 2^24; from now on fits into int32 + // |h2| <= 1.21*2^59 + // |h6| <= 1.21*2^59 + + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + // |h2| <= 2^25; from now on fits into int32 unchanged + // |h6| <= 2^25; from now on fits into int32 unchanged + // |h3| <= 1.51*2^58 + // |h7| <= 1.51*2^58 + + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + // |h3| <= 2^24; from now on fits into int32 unchanged + // |h7| <= 2^24; from now on fits into int32 unchanged + // |h4| <= 1.52*2^33 + // |h8| <= 1.52*2^33 + + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + // |h4| <= 2^25; from now on fits into int32 unchanged + // |h8| <= 2^25; from now on fits into int32 unchanged + // |h5| <= 1.01*2^24 + // |h9| <= 1.51*2^58 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + // |h9| <= 2^24; from now on fits into int32 unchanged + // |h0| <= 1.8*2^37 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + // |h0| <= 2^25; from now on fits into int32 unchanged + // |h1| <= 1.01*2^24 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feSquare calculates h = f*f. Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func feSquare(h, f *fieldElement) { + f0 := f[0] + f1 := f[1] + f2 := f[2] + f3 := f[3] + f4 := f[4] + f5 := f[5] + f6 := f[6] + f7 := f[7] + f8 := f[8] + f9 := f[9] + f0_2 := 2 * f0 + f1_2 := 2 * f1 + f2_2 := 2 * f2 + f3_2 := 2 * f3 + f4_2 := 2 * f4 + f5_2 := 2 * f5 + f6_2 := 2 * f6 + f7_2 := 2 * f7 + f5_38 := 38 * f5 // 1.31*2^30 + f6_19 := 19 * f6 // 1.31*2^30 + f7_38 := 38 * f7 // 1.31*2^30 + f8_19 := 19 * f8 // 1.31*2^30 + f9_38 := 38 * f9 // 1.31*2^30 + f0f0 := int64(f0) * int64(f0) + f0f1_2 := int64(f0_2) * int64(f1) + f0f2_2 := int64(f0_2) * int64(f2) + f0f3_2 := int64(f0_2) * int64(f3) + f0f4_2 := int64(f0_2) * int64(f4) + f0f5_2 := int64(f0_2) * int64(f5) + f0f6_2 := int64(f0_2) * int64(f6) + f0f7_2 := int64(f0_2) * int64(f7) + f0f8_2 := int64(f0_2) * int64(f8) + f0f9_2 := int64(f0_2) * int64(f9) + f1f1_2 := int64(f1_2) * int64(f1) + f1f2_2 := int64(f1_2) * int64(f2) + f1f3_4 := int64(f1_2) * int64(f3_2) + f1f4_2 := int64(f1_2) * int64(f4) + f1f5_4 := int64(f1_2) * int64(f5_2) + f1f6_2 := int64(f1_2) * int64(f6) + f1f7_4 := int64(f1_2) * int64(f7_2) + f1f8_2 := int64(f1_2) * int64(f8) + f1f9_76 := int64(f1_2) * int64(f9_38) + f2f2 := int64(f2) * int64(f2) + f2f3_2 := int64(f2_2) * int64(f3) + f2f4_2 := int64(f2_2) * int64(f4) + f2f5_2 := int64(f2_2) * int64(f5) + f2f6_2 := int64(f2_2) * int64(f6) + f2f7_2 := int64(f2_2) * int64(f7) + f2f8_38 := int64(f2_2) * int64(f8_19) + f2f9_38 := int64(f2) * int64(f9_38) + f3f3_2 := int64(f3_2) * int64(f3) + f3f4_2 := int64(f3_2) * int64(f4) + f3f5_4 := int64(f3_2) * int64(f5_2) + f3f6_2 := int64(f3_2) * int64(f6) + f3f7_76 := int64(f3_2) * int64(f7_38) + f3f8_38 := int64(f3_2) * int64(f8_19) + f3f9_76 := int64(f3_2) * int64(f9_38) + f4f4 := int64(f4) * int64(f4) + f4f5_2 := int64(f4_2) * int64(f5) + f4f6_38 := int64(f4_2) * int64(f6_19) + f4f7_38 := int64(f4) * int64(f7_38) + f4f8_38 := int64(f4_2) * int64(f8_19) + f4f9_38 := int64(f4) * int64(f9_38) + f5f5_38 := int64(f5) * int64(f5_38) + f5f6_38 := int64(f5_2) * int64(f6_19) + f5f7_76 := int64(f5_2) * int64(f7_38) + f5f8_38 := int64(f5_2) * int64(f8_19) + f5f9_76 := int64(f5_2) * int64(f9_38) + f6f6_19 := int64(f6) * int64(f6_19) + f6f7_38 := int64(f6) * int64(f7_38) + f6f8_38 := int64(f6_2) * int64(f8_19) + f6f9_38 := int64(f6) * int64(f9_38) + f7f7_38 := int64(f7) * int64(f7_38) + f7f8_38 := int64(f7_2) * int64(f8_19) + f7f9_76 := int64(f7_2) * int64(f9_38) + f8f8_19 := int64(f8) * int64(f8_19) + f8f9_38 := int64(f8) * int64(f9_38) + f9f9_38 := int64(f9) * int64(f9_38) + h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38 + h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38 + h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19 + h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38 + h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38 + h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38 + h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19 + h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38 + h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38 + h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2 + var carry [10]int64 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feMul121666 calculates h = f * 121666. Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func feMul121666(h, f *fieldElement) { + h0 := int64(f[0]) * 121666 + h1 := int64(f[1]) * 121666 + h2 := int64(f[2]) * 121666 + h3 := int64(f[3]) * 121666 + h4 := int64(f[4]) * 121666 + h5 := int64(f[5]) * 121666 + h6 := int64(f[6]) * 121666 + h7 := int64(f[7]) * 121666 + h8 := int64(f[8]) * 121666 + h9 := int64(f[9]) * 121666 + var carry [10]int64 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feInvert sets out = z^-1. +func feInvert(out, z *fieldElement) { + var t0, t1, t2, t3 fieldElement + var i int + + feSquare(&t0, z) + for i = 1; i < 1; i++ { + feSquare(&t0, &t0) + } + feSquare(&t1, &t0) + for i = 1; i < 2; i++ { + feSquare(&t1, &t1) + } + feMul(&t1, z, &t1) + feMul(&t0, &t0, &t1) + feSquare(&t2, &t0) + for i = 1; i < 1; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t1, &t2) + feSquare(&t2, &t1) + for i = 1; i < 5; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t2, &t1) + for i = 1; i < 10; i++ { + feSquare(&t2, &t2) + } + feMul(&t2, &t2, &t1) + feSquare(&t3, &t2) + for i = 1; i < 20; i++ { + feSquare(&t3, &t3) + } + feMul(&t2, &t3, &t2) + feSquare(&t2, &t2) + for i = 1; i < 10; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t2, &t1) + for i = 1; i < 50; i++ { + feSquare(&t2, &t2) + } + feMul(&t2, &t2, &t1) + feSquare(&t3, &t2) + for i = 1; i < 100; i++ { + feSquare(&t3, &t3) + } + feMul(&t2, &t3, &t2) + feSquare(&t2, &t2) + for i = 1; i < 50; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t1, &t1) + for i = 1; i < 5; i++ { + feSquare(&t1, &t1) + } + feMul(out, &t1, &t0) +} + +func scalarMult(out, in, base *[32]byte) { + var e [32]byte + + copy(e[:], in[:]) + e[0] &= 248 + e[31] &= 127 + e[31] |= 64 + + var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement + feFromBytes(&x1, base) + feOne(&x2) + feCopy(&x3, &x1) + feOne(&z3) + + swap := int32(0) + for pos := 254; pos >= 0; pos-- { + b := e[pos/8] >> uint(pos&7) + b &= 1 + swap ^= int32(b) + feCSwap(&x2, &x3, swap) + feCSwap(&z2, &z3, swap) + swap = int32(b) + + feSub(&tmp0, &x3, &z3) + feSub(&tmp1, &x2, &z2) + feAdd(&x2, &x2, &z2) + feAdd(&z2, &x3, &z3) + feMul(&z3, &tmp0, &x2) + feMul(&z2, &z2, &tmp1) + feSquare(&tmp0, &tmp1) + feSquare(&tmp1, &x2) + feAdd(&x3, &z3, &z2) + feSub(&z2, &z3, &z2) + feMul(&x2, &tmp1, &tmp0) + feSub(&tmp1, &tmp1, &tmp0) + feSquare(&z2, &z2) + feMul121666(&z3, &tmp1) + feSquare(&x3, &x3) + feAdd(&tmp0, &tmp0, &z3) + feMul(&z3, &x1, &z2) + feMul(&z2, &tmp1, &tmp0) + } + + feCSwap(&x2, &x3, swap) + feCSwap(&z2, &z3, swap) + + feInvert(&z2, &z2) + feMul(&x2, &x2, &z2) + feToBytes(out, &x2) +} diff --git a/vendor/golang.org/x/crypto/curve25519/doc.go b/vendor/golang.org/x/crypto/curve25519/doc.go new file mode 100644 index 000000000..ebeea3c2d --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/doc.go @@ -0,0 +1,23 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package curve25519 provides an implementation of scalar multiplication on +// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html +package curve25519 // import "golang.org/x/crypto/curve25519" + +// basePoint is the x coordinate of the generator of the curve. +var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +// ScalarMult sets dst to the product in*base where dst and base are the x +// coordinates of group points and all values are in little-endian form. +func ScalarMult(dst, in, base *[32]byte) { + scalarMult(dst, in, base) +} + +// ScalarBaseMult sets dst to the product in*base where dst and base are the x +// coordinates of group points, base is the standard generator and all values +// are in little-endian form. +func ScalarBaseMult(dst, in *[32]byte) { + ScalarMult(dst, in, &basePoint) +} diff --git a/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s b/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s new file mode 100644 index 000000000..536479bf6 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s @@ -0,0 +1,73 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine + +#include "const_amd64.h" + +// func freeze(inout *[5]uint64) +TEXT ·freeze(SB),7,$0-8 + MOVQ inout+0(FP), DI + + MOVQ 0(DI),SI + MOVQ 8(DI),DX + MOVQ 16(DI),CX + MOVQ 24(DI),R8 + MOVQ 32(DI),R9 + MOVQ $REDMASK51,AX + MOVQ AX,R10 + SUBQ $18,R10 + MOVQ $3,R11 +REDUCELOOP: + MOVQ SI,R12 + SHRQ $51,R12 + ANDQ AX,SI + ADDQ R12,DX + MOVQ DX,R12 + SHRQ $51,R12 + ANDQ AX,DX + ADDQ R12,CX + MOVQ CX,R12 + SHRQ $51,R12 + ANDQ AX,CX + ADDQ R12,R8 + MOVQ R8,R12 + SHRQ $51,R12 + ANDQ AX,R8 + ADDQ R12,R9 + MOVQ R9,R12 + SHRQ $51,R12 + ANDQ AX,R9 + IMUL3Q $19,R12,R12 + ADDQ R12,SI + SUBQ $1,R11 + JA REDUCELOOP + MOVQ $1,R12 + CMPQ R10,SI + CMOVQLT R11,R12 + CMPQ AX,DX + CMOVQNE R11,R12 + CMPQ AX,CX + CMOVQNE R11,R12 + CMPQ AX,R8 + CMOVQNE R11,R12 + CMPQ AX,R9 + CMOVQNE R11,R12 + NEGQ R12 + ANDQ R12,AX + ANDQ R12,R10 + SUBQ R10,SI + SUBQ AX,DX + SUBQ AX,CX + SUBQ AX,R8 + SUBQ AX,R9 + MOVQ SI,0(DI) + MOVQ DX,8(DI) + MOVQ CX,16(DI) + MOVQ R8,24(DI) + MOVQ R9,32(DI) + RET diff --git a/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s b/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s new file mode 100644 index 000000000..7074e5cd9 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s @@ -0,0 +1,1377 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine + +#include "const_amd64.h" + +// func ladderstep(inout *[5][5]uint64) +TEXT ·ladderstep(SB),0,$296-8 + MOVQ inout+0(FP),DI + + MOVQ 40(DI),SI + MOVQ 48(DI),DX + MOVQ 56(DI),CX + MOVQ 64(DI),R8 + MOVQ 72(DI),R9 + MOVQ SI,AX + MOVQ DX,R10 + MOVQ CX,R11 + MOVQ R8,R12 + MOVQ R9,R13 + ADDQ ·_2P0(SB),AX + ADDQ ·_2P1234(SB),R10 + ADDQ ·_2P1234(SB),R11 + ADDQ ·_2P1234(SB),R12 + ADDQ ·_2P1234(SB),R13 + ADDQ 80(DI),SI + ADDQ 88(DI),DX + ADDQ 96(DI),CX + ADDQ 104(DI),R8 + ADDQ 112(DI),R9 + SUBQ 80(DI),AX + SUBQ 88(DI),R10 + SUBQ 96(DI),R11 + SUBQ 104(DI),R12 + SUBQ 112(DI),R13 + MOVQ SI,0(SP) + MOVQ DX,8(SP) + MOVQ CX,16(SP) + MOVQ R8,24(SP) + MOVQ R9,32(SP) + MOVQ AX,40(SP) + MOVQ R10,48(SP) + MOVQ R11,56(SP) + MOVQ R12,64(SP) + MOVQ R13,72(SP) + MOVQ 40(SP),AX + MULQ 40(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 40(SP),AX + SHLQ $1,AX + MULQ 48(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 40(SP),AX + SHLQ $1,AX + MULQ 56(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 40(SP),AX + SHLQ $1,AX + MULQ 64(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 40(SP),AX + SHLQ $1,AX + MULQ 72(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 48(SP),AX + MULQ 48(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 48(SP),AX + SHLQ $1,AX + MULQ 56(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 48(SP),AX + SHLQ $1,AX + MULQ 64(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 48(SP),DX + IMUL3Q $38,DX,AX + MULQ 72(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 56(SP),AX + MULQ 56(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 56(SP),DX + IMUL3Q $38,DX,AX + MULQ 64(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 56(SP),DX + IMUL3Q $38,DX,AX + MULQ 72(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 64(SP),DX + IMUL3Q $19,DX,AX + MULQ 64(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 64(SP),DX + IMUL3Q $38,DX,AX + MULQ 72(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 72(SP),DX + IMUL3Q $19,DX,AX + MULQ 72(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + ANDQ DX,SI + MOVQ CX,R8 + SHRQ $51,CX + ADDQ R10,CX + ANDQ DX,R8 + MOVQ CX,R9 + SHRQ $51,CX + ADDQ R12,CX + ANDQ DX,R9 + MOVQ CX,AX + SHRQ $51,CX + ADDQ R14,CX + ANDQ DX,AX + MOVQ CX,R10 + SHRQ $51,CX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,80(SP) + MOVQ R8,88(SP) + MOVQ R9,96(SP) + MOVQ AX,104(SP) + MOVQ R10,112(SP) + MOVQ 0(SP),AX + MULQ 0(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 0(SP),AX + SHLQ $1,AX + MULQ 8(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 0(SP),AX + SHLQ $1,AX + MULQ 16(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 0(SP),AX + SHLQ $1,AX + MULQ 24(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 0(SP),AX + SHLQ $1,AX + MULQ 32(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 8(SP),AX + MULQ 8(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + SHLQ $1,AX + MULQ 16(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 8(SP),AX + SHLQ $1,AX + MULQ 24(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SP),DX + IMUL3Q $38,DX,AX + MULQ 32(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 16(SP),AX + MULQ 16(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 16(SP),DX + IMUL3Q $38,DX,AX + MULQ 24(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 16(SP),DX + IMUL3Q $38,DX,AX + MULQ 32(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 24(SP),DX + IMUL3Q $19,DX,AX + MULQ 24(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 24(SP),DX + IMUL3Q $38,DX,AX + MULQ 32(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 32(SP),DX + IMUL3Q $19,DX,AX + MULQ 32(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + ANDQ DX,SI + MOVQ CX,R8 + SHRQ $51,CX + ADDQ R10,CX + ANDQ DX,R8 + MOVQ CX,R9 + SHRQ $51,CX + ADDQ R12,CX + ANDQ DX,R9 + MOVQ CX,AX + SHRQ $51,CX + ADDQ R14,CX + ANDQ DX,AX + MOVQ CX,R10 + SHRQ $51,CX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,120(SP) + MOVQ R8,128(SP) + MOVQ R9,136(SP) + MOVQ AX,144(SP) + MOVQ R10,152(SP) + MOVQ SI,SI + MOVQ R8,DX + MOVQ R9,CX + MOVQ AX,R8 + MOVQ R10,R9 + ADDQ ·_2P0(SB),SI + ADDQ ·_2P1234(SB),DX + ADDQ ·_2P1234(SB),CX + ADDQ ·_2P1234(SB),R8 + ADDQ ·_2P1234(SB),R9 + SUBQ 80(SP),SI + SUBQ 88(SP),DX + SUBQ 96(SP),CX + SUBQ 104(SP),R8 + SUBQ 112(SP),R9 + MOVQ SI,160(SP) + MOVQ DX,168(SP) + MOVQ CX,176(SP) + MOVQ R8,184(SP) + MOVQ R9,192(SP) + MOVQ 120(DI),SI + MOVQ 128(DI),DX + MOVQ 136(DI),CX + MOVQ 144(DI),R8 + MOVQ 152(DI),R9 + MOVQ SI,AX + MOVQ DX,R10 + MOVQ CX,R11 + MOVQ R8,R12 + MOVQ R9,R13 + ADDQ ·_2P0(SB),AX + ADDQ ·_2P1234(SB),R10 + ADDQ ·_2P1234(SB),R11 + ADDQ ·_2P1234(SB),R12 + ADDQ ·_2P1234(SB),R13 + ADDQ 160(DI),SI + ADDQ 168(DI),DX + ADDQ 176(DI),CX + ADDQ 184(DI),R8 + ADDQ 192(DI),R9 + SUBQ 160(DI),AX + SUBQ 168(DI),R10 + SUBQ 176(DI),R11 + SUBQ 184(DI),R12 + SUBQ 192(DI),R13 + MOVQ SI,200(SP) + MOVQ DX,208(SP) + MOVQ CX,216(SP) + MOVQ R8,224(SP) + MOVQ R9,232(SP) + MOVQ AX,240(SP) + MOVQ R10,248(SP) + MOVQ R11,256(SP) + MOVQ R12,264(SP) + MOVQ R13,272(SP) + MOVQ 224(SP),SI + IMUL3Q $19,SI,AX + MOVQ AX,280(SP) + MULQ 56(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 232(SP),DX + IMUL3Q $19,DX,AX + MOVQ AX,288(SP) + MULQ 48(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 200(SP),AX + MULQ 40(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 200(SP),AX + MULQ 48(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 200(SP),AX + MULQ 56(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 200(SP),AX + MULQ 64(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 200(SP),AX + MULQ 72(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 208(SP),AX + MULQ 40(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 208(SP),AX + MULQ 48(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 208(SP),AX + MULQ 56(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 208(SP),AX + MULQ 64(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 208(SP),DX + IMUL3Q $19,DX,AX + MULQ 72(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 216(SP),AX + MULQ 40(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 216(SP),AX + MULQ 48(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 216(SP),AX + MULQ 56(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 216(SP),DX + IMUL3Q $19,DX,AX + MULQ 64(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 216(SP),DX + IMUL3Q $19,DX,AX + MULQ 72(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 224(SP),AX + MULQ 40(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 224(SP),AX + MULQ 48(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 280(SP),AX + MULQ 64(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 280(SP),AX + MULQ 72(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 232(SP),AX + MULQ 40(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 288(SP),AX + MULQ 56(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 288(SP),AX + MULQ 64(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 288(SP),AX + MULQ 72(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,40(SP) + MOVQ R8,48(SP) + MOVQ R9,56(SP) + MOVQ AX,64(SP) + MOVQ R10,72(SP) + MOVQ 264(SP),SI + IMUL3Q $19,SI,AX + MOVQ AX,200(SP) + MULQ 16(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 272(SP),DX + IMUL3Q $19,DX,AX + MOVQ AX,208(SP) + MULQ 8(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 240(SP),AX + MULQ 0(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 240(SP),AX + MULQ 8(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 240(SP),AX + MULQ 16(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 240(SP),AX + MULQ 24(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 240(SP),AX + MULQ 32(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 248(SP),AX + MULQ 0(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 248(SP),AX + MULQ 8(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 248(SP),AX + MULQ 16(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 248(SP),AX + MULQ 24(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 248(SP),DX + IMUL3Q $19,DX,AX + MULQ 32(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 256(SP),AX + MULQ 0(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 256(SP),AX + MULQ 8(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 256(SP),AX + MULQ 16(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 256(SP),DX + IMUL3Q $19,DX,AX + MULQ 24(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 256(SP),DX + IMUL3Q $19,DX,AX + MULQ 32(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 264(SP),AX + MULQ 0(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 264(SP),AX + MULQ 8(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 200(SP),AX + MULQ 24(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 200(SP),AX + MULQ 32(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 272(SP),AX + MULQ 0(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 208(SP),AX + MULQ 16(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 208(SP),AX + MULQ 24(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 208(SP),AX + MULQ 32(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,DX + MOVQ R8,CX + MOVQ R9,R11 + MOVQ AX,R12 + MOVQ R10,R13 + ADDQ ·_2P0(SB),DX + ADDQ ·_2P1234(SB),CX + ADDQ ·_2P1234(SB),R11 + ADDQ ·_2P1234(SB),R12 + ADDQ ·_2P1234(SB),R13 + ADDQ 40(SP),SI + ADDQ 48(SP),R8 + ADDQ 56(SP),R9 + ADDQ 64(SP),AX + ADDQ 72(SP),R10 + SUBQ 40(SP),DX + SUBQ 48(SP),CX + SUBQ 56(SP),R11 + SUBQ 64(SP),R12 + SUBQ 72(SP),R13 + MOVQ SI,120(DI) + MOVQ R8,128(DI) + MOVQ R9,136(DI) + MOVQ AX,144(DI) + MOVQ R10,152(DI) + MOVQ DX,160(DI) + MOVQ CX,168(DI) + MOVQ R11,176(DI) + MOVQ R12,184(DI) + MOVQ R13,192(DI) + MOVQ 120(DI),AX + MULQ 120(DI) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 120(DI),AX + SHLQ $1,AX + MULQ 128(DI) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 120(DI),AX + SHLQ $1,AX + MULQ 136(DI) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 120(DI),AX + SHLQ $1,AX + MULQ 144(DI) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 120(DI),AX + SHLQ $1,AX + MULQ 152(DI) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 128(DI),AX + MULQ 128(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 128(DI),AX + SHLQ $1,AX + MULQ 136(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 128(DI),AX + SHLQ $1,AX + MULQ 144(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 128(DI),DX + IMUL3Q $38,DX,AX + MULQ 152(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 136(DI),AX + MULQ 136(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 136(DI),DX + IMUL3Q $38,DX,AX + MULQ 144(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 136(DI),DX + IMUL3Q $38,DX,AX + MULQ 152(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 144(DI),DX + IMUL3Q $19,DX,AX + MULQ 144(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 144(DI),DX + IMUL3Q $38,DX,AX + MULQ 152(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 152(DI),DX + IMUL3Q $19,DX,AX + MULQ 152(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + ANDQ DX,SI + MOVQ CX,R8 + SHRQ $51,CX + ADDQ R10,CX + ANDQ DX,R8 + MOVQ CX,R9 + SHRQ $51,CX + ADDQ R12,CX + ANDQ DX,R9 + MOVQ CX,AX + SHRQ $51,CX + ADDQ R14,CX + ANDQ DX,AX + MOVQ CX,R10 + SHRQ $51,CX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,120(DI) + MOVQ R8,128(DI) + MOVQ R9,136(DI) + MOVQ AX,144(DI) + MOVQ R10,152(DI) + MOVQ 160(DI),AX + MULQ 160(DI) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 160(DI),AX + SHLQ $1,AX + MULQ 168(DI) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 160(DI),AX + SHLQ $1,AX + MULQ 176(DI) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 160(DI),AX + SHLQ $1,AX + MULQ 184(DI) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 160(DI),AX + SHLQ $1,AX + MULQ 192(DI) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 168(DI),AX + MULQ 168(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 168(DI),AX + SHLQ $1,AX + MULQ 176(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 168(DI),AX + SHLQ $1,AX + MULQ 184(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 168(DI),DX + IMUL3Q $38,DX,AX + MULQ 192(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 176(DI),AX + MULQ 176(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 176(DI),DX + IMUL3Q $38,DX,AX + MULQ 184(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 176(DI),DX + IMUL3Q $38,DX,AX + MULQ 192(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 184(DI),DX + IMUL3Q $19,DX,AX + MULQ 184(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 184(DI),DX + IMUL3Q $38,DX,AX + MULQ 192(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 192(DI),DX + IMUL3Q $19,DX,AX + MULQ 192(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + ANDQ DX,SI + MOVQ CX,R8 + SHRQ $51,CX + ADDQ R10,CX + ANDQ DX,R8 + MOVQ CX,R9 + SHRQ $51,CX + ADDQ R12,CX + ANDQ DX,R9 + MOVQ CX,AX + SHRQ $51,CX + ADDQ R14,CX + ANDQ DX,AX + MOVQ CX,R10 + SHRQ $51,CX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,160(DI) + MOVQ R8,168(DI) + MOVQ R9,176(DI) + MOVQ AX,184(DI) + MOVQ R10,192(DI) + MOVQ 184(DI),SI + IMUL3Q $19,SI,AX + MOVQ AX,0(SP) + MULQ 16(DI) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 192(DI),DX + IMUL3Q $19,DX,AX + MOVQ AX,8(SP) + MULQ 8(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 160(DI),AX + MULQ 0(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 160(DI),AX + MULQ 8(DI) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 160(DI),AX + MULQ 16(DI) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 160(DI),AX + MULQ 24(DI) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 160(DI),AX + MULQ 32(DI) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 168(DI),AX + MULQ 0(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 168(DI),AX + MULQ 8(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 168(DI),AX + MULQ 16(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 168(DI),AX + MULQ 24(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 168(DI),DX + IMUL3Q $19,DX,AX + MULQ 32(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 176(DI),AX + MULQ 0(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 176(DI),AX + MULQ 8(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 176(DI),AX + MULQ 16(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 176(DI),DX + IMUL3Q $19,DX,AX + MULQ 24(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 176(DI),DX + IMUL3Q $19,DX,AX + MULQ 32(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 184(DI),AX + MULQ 0(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 184(DI),AX + MULQ 8(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 0(SP),AX + MULQ 24(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SP),AX + MULQ 32(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 192(DI),AX + MULQ 0(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SP),AX + MULQ 16(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 8(SP),AX + MULQ 24(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + MULQ 32(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,160(DI) + MOVQ R8,168(DI) + MOVQ R9,176(DI) + MOVQ AX,184(DI) + MOVQ R10,192(DI) + MOVQ 144(SP),SI + IMUL3Q $19,SI,AX + MOVQ AX,0(SP) + MULQ 96(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 152(SP),DX + IMUL3Q $19,DX,AX + MOVQ AX,8(SP) + MULQ 88(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 120(SP),AX + MULQ 80(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 120(SP),AX + MULQ 88(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 120(SP),AX + MULQ 96(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 120(SP),AX + MULQ 104(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 120(SP),AX + MULQ 112(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 128(SP),AX + MULQ 80(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 128(SP),AX + MULQ 88(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 128(SP),AX + MULQ 96(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 128(SP),AX + MULQ 104(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 128(SP),DX + IMUL3Q $19,DX,AX + MULQ 112(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 136(SP),AX + MULQ 80(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 136(SP),AX + MULQ 88(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 136(SP),AX + MULQ 96(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 136(SP),DX + IMUL3Q $19,DX,AX + MULQ 104(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 136(SP),DX + IMUL3Q $19,DX,AX + MULQ 112(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 144(SP),AX + MULQ 80(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 144(SP),AX + MULQ 88(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 0(SP),AX + MULQ 104(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SP),AX + MULQ 112(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 152(SP),AX + MULQ 80(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SP),AX + MULQ 96(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 8(SP),AX + MULQ 104(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + MULQ 112(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,40(DI) + MOVQ R8,48(DI) + MOVQ R9,56(DI) + MOVQ AX,64(DI) + MOVQ R10,72(DI) + MOVQ 160(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + MOVQ AX,SI + MOVQ DX,CX + MOVQ 168(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + ADDQ AX,CX + MOVQ DX,R8 + MOVQ 176(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + ADDQ AX,R8 + MOVQ DX,R9 + MOVQ 184(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + ADDQ AX,R9 + MOVQ DX,R10 + MOVQ 192(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + ADDQ AX,R10 + IMUL3Q $19,DX,DX + ADDQ DX,SI + ADDQ 80(SP),SI + ADDQ 88(SP),CX + ADDQ 96(SP),R8 + ADDQ 104(SP),R9 + ADDQ 112(SP),R10 + MOVQ SI,80(DI) + MOVQ CX,88(DI) + MOVQ R8,96(DI) + MOVQ R9,104(DI) + MOVQ R10,112(DI) + MOVQ 104(DI),SI + IMUL3Q $19,SI,AX + MOVQ AX,0(SP) + MULQ 176(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 112(DI),DX + IMUL3Q $19,DX,AX + MOVQ AX,8(SP) + MULQ 168(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 80(DI),AX + MULQ 160(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 80(DI),AX + MULQ 168(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 80(DI),AX + MULQ 176(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 80(DI),AX + MULQ 184(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 80(DI),AX + MULQ 192(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 88(DI),AX + MULQ 160(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 88(DI),AX + MULQ 168(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 88(DI),AX + MULQ 176(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 88(DI),AX + MULQ 184(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 88(DI),DX + IMUL3Q $19,DX,AX + MULQ 192(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 96(DI),AX + MULQ 160(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 96(DI),AX + MULQ 168(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 96(DI),AX + MULQ 176(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 96(DI),DX + IMUL3Q $19,DX,AX + MULQ 184(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 96(DI),DX + IMUL3Q $19,DX,AX + MULQ 192(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 104(DI),AX + MULQ 160(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 104(DI),AX + MULQ 168(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 0(SP),AX + MULQ 184(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SP),AX + MULQ 192(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 112(DI),AX + MULQ 160(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SP),AX + MULQ 176(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 8(SP),AX + MULQ 184(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + MULQ 192(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,CX:SI + ANDQ DX,SI + SHLQ $13,R9:R8 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R11:R10 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,80(DI) + MOVQ R8,88(DI) + MOVQ R9,96(DI) + MOVQ AX,104(DI) + MOVQ R10,112(DI) + RET diff --git a/vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go b/vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go new file mode 100644 index 000000000..5822bd533 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go @@ -0,0 +1,240 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine + +package curve25519 + +// These functions are implemented in the .s files. The names of the functions +// in the rest of the file are also taken from the SUPERCOP sources to help +// people following along. + +//go:noescape + +func cswap(inout *[5]uint64, v uint64) + +//go:noescape + +func ladderstep(inout *[5][5]uint64) + +//go:noescape + +func freeze(inout *[5]uint64) + +//go:noescape + +func mul(dest, a, b *[5]uint64) + +//go:noescape + +func square(out, in *[5]uint64) + +// mladder uses a Montgomery ladder to calculate (xr/zr) *= s. +func mladder(xr, zr *[5]uint64, s *[32]byte) { + var work [5][5]uint64 + + work[0] = *xr + setint(&work[1], 1) + setint(&work[2], 0) + work[3] = *xr + setint(&work[4], 1) + + j := uint(6) + var prevbit byte + + for i := 31; i >= 0; i-- { + for j < 8 { + bit := ((*s)[i] >> j) & 1 + swap := bit ^ prevbit + prevbit = bit + cswap(&work[1], uint64(swap)) + ladderstep(&work) + j-- + } + j = 7 + } + + *xr = work[1] + *zr = work[2] +} + +func scalarMult(out, in, base *[32]byte) { + var e [32]byte + copy(e[:], (*in)[:]) + e[0] &= 248 + e[31] &= 127 + e[31] |= 64 + + var t, z [5]uint64 + unpack(&t, base) + mladder(&t, &z, &e) + invert(&z, &z) + mul(&t, &t, &z) + pack(out, &t) +} + +func setint(r *[5]uint64, v uint64) { + r[0] = v + r[1] = 0 + r[2] = 0 + r[3] = 0 + r[4] = 0 +} + +// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian +// order. +func unpack(r *[5]uint64, x *[32]byte) { + r[0] = uint64(x[0]) | + uint64(x[1])<<8 | + uint64(x[2])<<16 | + uint64(x[3])<<24 | + uint64(x[4])<<32 | + uint64(x[5])<<40 | + uint64(x[6]&7)<<48 + + r[1] = uint64(x[6])>>3 | + uint64(x[7])<<5 | + uint64(x[8])<<13 | + uint64(x[9])<<21 | + uint64(x[10])<<29 | + uint64(x[11])<<37 | + uint64(x[12]&63)<<45 + + r[2] = uint64(x[12])>>6 | + uint64(x[13])<<2 | + uint64(x[14])<<10 | + uint64(x[15])<<18 | + uint64(x[16])<<26 | + uint64(x[17])<<34 | + uint64(x[18])<<42 | + uint64(x[19]&1)<<50 + + r[3] = uint64(x[19])>>1 | + uint64(x[20])<<7 | + uint64(x[21])<<15 | + uint64(x[22])<<23 | + uint64(x[23])<<31 | + uint64(x[24])<<39 | + uint64(x[25]&15)<<47 + + r[4] = uint64(x[25])>>4 | + uint64(x[26])<<4 | + uint64(x[27])<<12 | + uint64(x[28])<<20 | + uint64(x[29])<<28 | + uint64(x[30])<<36 | + uint64(x[31]&127)<<44 +} + +// pack sets out = x where out is the usual, little-endian form of the 5, +// 51-bit limbs in x. +func pack(out *[32]byte, x *[5]uint64) { + t := *x + freeze(&t) + + out[0] = byte(t[0]) + out[1] = byte(t[0] >> 8) + out[2] = byte(t[0] >> 16) + out[3] = byte(t[0] >> 24) + out[4] = byte(t[0] >> 32) + out[5] = byte(t[0] >> 40) + out[6] = byte(t[0] >> 48) + + out[6] ^= byte(t[1]<<3) & 0xf8 + out[7] = byte(t[1] >> 5) + out[8] = byte(t[1] >> 13) + out[9] = byte(t[1] >> 21) + out[10] = byte(t[1] >> 29) + out[11] = byte(t[1] >> 37) + out[12] = byte(t[1] >> 45) + + out[12] ^= byte(t[2]<<6) & 0xc0 + out[13] = byte(t[2] >> 2) + out[14] = byte(t[2] >> 10) + out[15] = byte(t[2] >> 18) + out[16] = byte(t[2] >> 26) + out[17] = byte(t[2] >> 34) + out[18] = byte(t[2] >> 42) + out[19] = byte(t[2] >> 50) + + out[19] ^= byte(t[3]<<1) & 0xfe + out[20] = byte(t[3] >> 7) + out[21] = byte(t[3] >> 15) + out[22] = byte(t[3] >> 23) + out[23] = byte(t[3] >> 31) + out[24] = byte(t[3] >> 39) + out[25] = byte(t[3] >> 47) + + out[25] ^= byte(t[4]<<4) & 0xf0 + out[26] = byte(t[4] >> 4) + out[27] = byte(t[4] >> 12) + out[28] = byte(t[4] >> 20) + out[29] = byte(t[4] >> 28) + out[30] = byte(t[4] >> 36) + out[31] = byte(t[4] >> 44) +} + +// invert calculates r = x^-1 mod p using Fermat's little theorem. +func invert(r *[5]uint64, x *[5]uint64) { + var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 + + square(&z2, x) /* 2 */ + square(&t, &z2) /* 4 */ + square(&t, &t) /* 8 */ + mul(&z9, &t, x) /* 9 */ + mul(&z11, &z9, &z2) /* 11 */ + square(&t, &z11) /* 22 */ + mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ + + square(&t, &z2_5_0) /* 2^6 - 2^1 */ + for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ + square(&t, &t) + } + mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ + + square(&t, &z2_10_0) /* 2^11 - 2^1 */ + for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ + square(&t, &t) + } + mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ + + square(&t, &z2_20_0) /* 2^21 - 2^1 */ + for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ + square(&t, &t) + } + mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ + + square(&t, &t) /* 2^41 - 2^1 */ + for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ + square(&t, &t) + } + mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ + + square(&t, &z2_50_0) /* 2^51 - 2^1 */ + for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ + square(&t, &t) + } + mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ + + square(&t, &z2_100_0) /* 2^101 - 2^1 */ + for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ + square(&t, &t) + } + mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ + + square(&t, &t) /* 2^201 - 2^1 */ + for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ + square(&t, &t) + } + mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ + + square(&t, &t) /* 2^251 - 2^1 */ + square(&t, &t) /* 2^252 - 2^2 */ + square(&t, &t) /* 2^253 - 2^3 */ + + square(&t, &t) /* 2^254 - 2^4 */ + + square(&t, &t) /* 2^255 - 2^5 */ + mul(r, &t, &z11) /* 2^255 - 21 */ +} diff --git a/vendor/golang.org/x/crypto/curve25519/mul_amd64.s b/vendor/golang.org/x/crypto/curve25519/mul_amd64.s new file mode 100644 index 000000000..b162e6515 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/mul_amd64.s @@ -0,0 +1,169 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine + +#include "const_amd64.h" + +// func mul(dest, a, b *[5]uint64) +TEXT ·mul(SB),0,$16-24 + MOVQ dest+0(FP), DI + MOVQ a+8(FP), SI + MOVQ b+16(FP), DX + + MOVQ DX,CX + MOVQ 24(SI),DX + IMUL3Q $19,DX,AX + MOVQ AX,0(SP) + MULQ 16(CX) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 32(SI),DX + IMUL3Q $19,DX,AX + MOVQ AX,8(SP) + MULQ 8(CX) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SI),AX + MULQ 0(CX) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SI),AX + MULQ 8(CX) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 0(SI),AX + MULQ 16(CX) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 0(SI),AX + MULQ 24(CX) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 0(SI),AX + MULQ 32(CX) + MOVQ AX,BX + MOVQ DX,BP + MOVQ 8(SI),AX + MULQ 0(CX) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SI),AX + MULQ 8(CX) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 8(SI),AX + MULQ 16(CX) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SI),AX + MULQ 24(CX) + ADDQ AX,BX + ADCQ DX,BP + MOVQ 8(SI),DX + IMUL3Q $19,DX,AX + MULQ 32(CX) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 16(SI),AX + MULQ 0(CX) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 16(SI),AX + MULQ 8(CX) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 16(SI),AX + MULQ 16(CX) + ADDQ AX,BX + ADCQ DX,BP + MOVQ 16(SI),DX + IMUL3Q $19,DX,AX + MULQ 24(CX) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 16(SI),DX + IMUL3Q $19,DX,AX + MULQ 32(CX) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 24(SI),AX + MULQ 0(CX) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 24(SI),AX + MULQ 8(CX) + ADDQ AX,BX + ADCQ DX,BP + MOVQ 0(SP),AX + MULQ 24(CX) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 0(SP),AX + MULQ 32(CX) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 32(SI),AX + MULQ 0(CX) + ADDQ AX,BX + ADCQ DX,BP + MOVQ 8(SP),AX + MULQ 16(CX) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + MULQ 24(CX) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 8(SP),AX + MULQ 32(CX) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ $REDMASK51,SI + SHLQ $13,R9:R8 + ANDQ SI,R8 + SHLQ $13,R11:R10 + ANDQ SI,R10 + ADDQ R9,R10 + SHLQ $13,R13:R12 + ANDQ SI,R12 + ADDQ R11,R12 + SHLQ $13,R15:R14 + ANDQ SI,R14 + ADDQ R13,R14 + SHLQ $13,BP:BX + ANDQ SI,BX + ADDQ R15,BX + IMUL3Q $19,BP,DX + ADDQ DX,R8 + MOVQ R8,DX + SHRQ $51,DX + ADDQ R10,DX + MOVQ DX,CX + SHRQ $51,DX + ANDQ SI,R8 + ADDQ R12,DX + MOVQ DX,R9 + SHRQ $51,DX + ANDQ SI,CX + ADDQ R14,DX + MOVQ DX,AX + SHRQ $51,DX + ANDQ SI,R9 + ADDQ BX,DX + MOVQ DX,R10 + SHRQ $51,DX + ANDQ SI,AX + IMUL3Q $19,DX,DX + ADDQ DX,R8 + ANDQ SI,R10 + MOVQ R8,0(DI) + MOVQ CX,8(DI) + MOVQ R9,16(DI) + MOVQ AX,24(DI) + MOVQ R10,32(DI) + RET diff --git a/vendor/golang.org/x/crypto/curve25519/square_amd64.s b/vendor/golang.org/x/crypto/curve25519/square_amd64.s new file mode 100644 index 000000000..4e864a83e --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/square_amd64.s @@ -0,0 +1,132 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine + +#include "const_amd64.h" + +// func square(out, in *[5]uint64) +TEXT ·square(SB),7,$0-16 + MOVQ out+0(FP), DI + MOVQ in+8(FP), SI + + MOVQ 0(SI),AX + MULQ 0(SI) + MOVQ AX,CX + MOVQ DX,R8 + MOVQ 0(SI),AX + SHLQ $1,AX + MULQ 8(SI) + MOVQ AX,R9 + MOVQ DX,R10 + MOVQ 0(SI),AX + SHLQ $1,AX + MULQ 16(SI) + MOVQ AX,R11 + MOVQ DX,R12 + MOVQ 0(SI),AX + SHLQ $1,AX + MULQ 24(SI) + MOVQ AX,R13 + MOVQ DX,R14 + MOVQ 0(SI),AX + SHLQ $1,AX + MULQ 32(SI) + MOVQ AX,R15 + MOVQ DX,BX + MOVQ 8(SI),AX + MULQ 8(SI) + ADDQ AX,R11 + ADCQ DX,R12 + MOVQ 8(SI),AX + SHLQ $1,AX + MULQ 16(SI) + ADDQ AX,R13 + ADCQ DX,R14 + MOVQ 8(SI),AX + SHLQ $1,AX + MULQ 24(SI) + ADDQ AX,R15 + ADCQ DX,BX + MOVQ 8(SI),DX + IMUL3Q $38,DX,AX + MULQ 32(SI) + ADDQ AX,CX + ADCQ DX,R8 + MOVQ 16(SI),AX + MULQ 16(SI) + ADDQ AX,R15 + ADCQ DX,BX + MOVQ 16(SI),DX + IMUL3Q $38,DX,AX + MULQ 24(SI) + ADDQ AX,CX + ADCQ DX,R8 + MOVQ 16(SI),DX + IMUL3Q $38,DX,AX + MULQ 32(SI) + ADDQ AX,R9 + ADCQ DX,R10 + MOVQ 24(SI),DX + IMUL3Q $19,DX,AX + MULQ 24(SI) + ADDQ AX,R9 + ADCQ DX,R10 + MOVQ 24(SI),DX + IMUL3Q $38,DX,AX + MULQ 32(SI) + ADDQ AX,R11 + ADCQ DX,R12 + MOVQ 32(SI),DX + IMUL3Q $19,DX,AX + MULQ 32(SI) + ADDQ AX,R13 + ADCQ DX,R14 + MOVQ $REDMASK51,SI + SHLQ $13,R8:CX + ANDQ SI,CX + SHLQ $13,R10:R9 + ANDQ SI,R9 + ADDQ R8,R9 + SHLQ $13,R12:R11 + ANDQ SI,R11 + ADDQ R10,R11 + SHLQ $13,R14:R13 + ANDQ SI,R13 + ADDQ R12,R13 + SHLQ $13,BX:R15 + ANDQ SI,R15 + ADDQ R14,R15 + IMUL3Q $19,BX,DX + ADDQ DX,CX + MOVQ CX,DX + SHRQ $51,DX + ADDQ R9,DX + ANDQ SI,CX + MOVQ DX,R8 + SHRQ $51,DX + ADDQ R11,DX + ANDQ SI,R8 + MOVQ DX,R9 + SHRQ $51,DX + ADDQ R13,DX + ANDQ SI,R9 + MOVQ DX,AX + SHRQ $51,DX + ADDQ R15,DX + ANDQ SI,AX + MOVQ DX,R10 + SHRQ $51,DX + IMUL3Q $19,DX,DX + ADDQ DX,CX + ANDQ SI,R10 + MOVQ CX,0(DI) + MOVQ R8,8(DI) + MOVQ R9,16(DI) + MOVQ AX,24(DI) + MOVQ R10,32(DI) + RET diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519.go b/vendor/golang.org/x/crypto/ed25519/ed25519.go new file mode 100644 index 000000000..f1d95674a --- /dev/null +++ b/vendor/golang.org/x/crypto/ed25519/ed25519.go @@ -0,0 +1,181 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ed25519 implements the Ed25519 signature algorithm. See +// http://ed25519.cr.yp.to/. +// +// These functions are also compatible with the “Ed25519” function defined in +// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05. +package ed25519 + +// This code is a port of the public domain, “ref10” implementation of ed25519 +// from SUPERCOP. + +import ( + "crypto" + cryptorand "crypto/rand" + "crypto/sha512" + "crypto/subtle" + "errors" + "io" + "strconv" + + "golang.org/x/crypto/ed25519/internal/edwards25519" +) + +const ( + // PublicKeySize is the size, in bytes, of public keys as used in this package. + PublicKeySize = 32 + // PrivateKeySize is the size, in bytes, of private keys as used in this package. + PrivateKeySize = 64 + // SignatureSize is the size, in bytes, of signatures generated and verified by this package. + SignatureSize = 64 +) + +// PublicKey is the type of Ed25519 public keys. +type PublicKey []byte + +// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. +type PrivateKey []byte + +// Public returns the PublicKey corresponding to priv. +func (priv PrivateKey) Public() crypto.PublicKey { + publicKey := make([]byte, PublicKeySize) + copy(publicKey, priv[32:]) + return PublicKey(publicKey) +} + +// Sign signs the given message with priv. +// Ed25519 performs two passes over messages to be signed and therefore cannot +// handle pre-hashed messages. Thus opts.HashFunc() must return zero to +// indicate the message hasn't been hashed. This can be achieved by passing +// crypto.Hash(0) as the value for opts. +func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { + if opts.HashFunc() != crypto.Hash(0) { + return nil, errors.New("ed25519: cannot sign hashed message") + } + + return Sign(priv, message), nil +} + +// GenerateKey generates a public/private key pair using entropy from rand. +// If rand is nil, crypto/rand.Reader will be used. +func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) { + if rand == nil { + rand = cryptorand.Reader + } + + privateKey = make([]byte, PrivateKeySize) + publicKey = make([]byte, PublicKeySize) + _, err = io.ReadFull(rand, privateKey[:32]) + if err != nil { + return nil, nil, err + } + + digest := sha512.Sum512(privateKey[:32]) + digest[0] &= 248 + digest[31] &= 127 + digest[31] |= 64 + + var A edwards25519.ExtendedGroupElement + var hBytes [32]byte + copy(hBytes[:], digest[:]) + edwards25519.GeScalarMultBase(&A, &hBytes) + var publicKeyBytes [32]byte + A.ToBytes(&publicKeyBytes) + + copy(privateKey[32:], publicKeyBytes[:]) + copy(publicKey, publicKeyBytes[:]) + + return publicKey, privateKey, nil +} + +// Sign signs the message with privateKey and returns a signature. It will +// panic if len(privateKey) is not PrivateKeySize. +func Sign(privateKey PrivateKey, message []byte) []byte { + if l := len(privateKey); l != PrivateKeySize { + panic("ed25519: bad private key length: " + strconv.Itoa(l)) + } + + h := sha512.New() + h.Write(privateKey[:32]) + + var digest1, messageDigest, hramDigest [64]byte + var expandedSecretKey [32]byte + h.Sum(digest1[:0]) + copy(expandedSecretKey[:], digest1[:]) + expandedSecretKey[0] &= 248 + expandedSecretKey[31] &= 63 + expandedSecretKey[31] |= 64 + + h.Reset() + h.Write(digest1[32:]) + h.Write(message) + h.Sum(messageDigest[:0]) + + var messageDigestReduced [32]byte + edwards25519.ScReduce(&messageDigestReduced, &messageDigest) + var R edwards25519.ExtendedGroupElement + edwards25519.GeScalarMultBase(&R, &messageDigestReduced) + + var encodedR [32]byte + R.ToBytes(&encodedR) + + h.Reset() + h.Write(encodedR[:]) + h.Write(privateKey[32:]) + h.Write(message) + h.Sum(hramDigest[:0]) + var hramDigestReduced [32]byte + edwards25519.ScReduce(&hramDigestReduced, &hramDigest) + + var s [32]byte + edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced) + + signature := make([]byte, SignatureSize) + copy(signature[:], encodedR[:]) + copy(signature[32:], s[:]) + + return signature +} + +// Verify reports whether sig is a valid signature of message by publicKey. It +// will panic if len(publicKey) is not PublicKeySize. +func Verify(publicKey PublicKey, message, sig []byte) bool { + if l := len(publicKey); l != PublicKeySize { + panic("ed25519: bad public key length: " + strconv.Itoa(l)) + } + + if len(sig) != SignatureSize || sig[63]&224 != 0 { + return false + } + + var A edwards25519.ExtendedGroupElement + var publicKeyBytes [32]byte + copy(publicKeyBytes[:], publicKey) + if !A.FromBytes(&publicKeyBytes) { + return false + } + edwards25519.FeNeg(&A.X, &A.X) + edwards25519.FeNeg(&A.T, &A.T) + + h := sha512.New() + h.Write(sig[:32]) + h.Write(publicKey[:]) + h.Write(message) + var digest [64]byte + h.Sum(digest[:0]) + + var hReduced [32]byte + edwards25519.ScReduce(&hReduced, &digest) + + var R edwards25519.ProjectiveGroupElement + var b [32]byte + copy(b[:], sig[32:]) + edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b) + + var checkR [32]byte + R.ToBytes(&checkR) + return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1 +} diff --git a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go new file mode 100644 index 000000000..e39f086c1 --- /dev/null +++ b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go @@ -0,0 +1,1422 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package edwards25519 + +// These values are from the public domain, “ref10” implementation of ed25519 +// from SUPERCOP. + +// d is a constant in the Edwards curve equation. +var d = FieldElement{ + -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116, +} + +// d2 is 2*d. +var d2 = FieldElement{ + -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199, +} + +// SqrtM1 is the square-root of -1 in the field. +var SqrtM1 = FieldElement{ + -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482, +} + +// A is a constant in the Montgomery-form of curve25519. +var A = FieldElement{ + 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0, +} + +// bi contains precomputed multiples of the base-point. See the Ed25519 paper +// for a discussion about how these values are used. +var bi = [8]PreComputedGroupElement{ + { + FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, + FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, + FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}, + }, + { + FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024}, + FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574}, + FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}, + }, + { + FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380}, + FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306}, + FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}, + }, + { + FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766}, + FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701}, + FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}, + }, + { + FieldElement{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877}, + FieldElement{-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951}, + FieldElement{4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784}, + }, + { + FieldElement{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436}, + FieldElement{25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918}, + FieldElement{23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877}, + }, + { + FieldElement{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800}, + FieldElement{-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305}, + FieldElement{-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300}, + }, + { + FieldElement{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876}, + FieldElement{-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619}, + FieldElement{-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683}, + }, +} + +// base contains precomputed multiples of the base-point. See the Ed25519 paper +// for a discussion about how these values are used. +var base = [32][8]PreComputedGroupElement{ + { + { + FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, + FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, + FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}, + }, + { + FieldElement{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303}, + FieldElement{-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081}, + FieldElement{26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697}, + }, + { + FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024}, + FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574}, + FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}, + }, + { + FieldElement{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540}, + FieldElement{23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397}, + FieldElement{7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325}, + }, + { + FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380}, + FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306}, + FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}, + }, + { + FieldElement{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777}, + FieldElement{-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737}, + FieldElement{-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652}, + }, + { + FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766}, + FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701}, + FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}, + }, + { + FieldElement{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726}, + FieldElement{-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955}, + FieldElement{27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425}, + }, + }, + { + { + FieldElement{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171}, + FieldElement{27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510}, + FieldElement{17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660}, + }, + { + FieldElement{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639}, + FieldElement{29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963}, + FieldElement{5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950}, + }, + { + FieldElement{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568}, + FieldElement{12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335}, + FieldElement{25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628}, + }, + { + FieldElement{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007}, + FieldElement{-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772}, + FieldElement{-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653}, + }, + { + FieldElement{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567}, + FieldElement{13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686}, + FieldElement{21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372}, + }, + { + FieldElement{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887}, + FieldElement{-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954}, + FieldElement{-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953}, + }, + { + FieldElement{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833}, + FieldElement{-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532}, + FieldElement{-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876}, + }, + { + FieldElement{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268}, + FieldElement{33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214}, + FieldElement{1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038}, + }, + }, + { + { + FieldElement{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800}, + FieldElement{4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645}, + FieldElement{-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664}, + }, + { + FieldElement{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933}, + FieldElement{-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182}, + FieldElement{-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222}, + }, + { + FieldElement{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991}, + FieldElement{20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880}, + FieldElement{9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092}, + }, + { + FieldElement{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295}, + FieldElement{19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788}, + FieldElement{8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553}, + }, + { + FieldElement{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026}, + FieldElement{11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347}, + FieldElement{-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033}, + }, + { + FieldElement{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395}, + FieldElement{-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278}, + FieldElement{1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890}, + }, + { + FieldElement{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995}, + FieldElement{-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596}, + FieldElement{-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891}, + }, + { + FieldElement{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060}, + FieldElement{11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608}, + FieldElement{-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606}, + }, + }, + { + { + FieldElement{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389}, + FieldElement{-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016}, + FieldElement{-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341}, + }, + { + FieldElement{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505}, + FieldElement{14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553}, + FieldElement{-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655}, + }, + { + FieldElement{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220}, + FieldElement{12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631}, + FieldElement{-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099}, + }, + { + FieldElement{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556}, + FieldElement{14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749}, + FieldElement{236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930}, + }, + { + FieldElement{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391}, + FieldElement{5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253}, + FieldElement{20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066}, + }, + { + FieldElement{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958}, + FieldElement{-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082}, + FieldElement{-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383}, + }, + { + FieldElement{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521}, + FieldElement{-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807}, + FieldElement{23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948}, + }, + { + FieldElement{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134}, + FieldElement{-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455}, + FieldElement{27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629}, + }, + }, + { + { + FieldElement{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069}, + FieldElement{-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746}, + FieldElement{24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919}, + }, + { + FieldElement{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837}, + FieldElement{8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906}, + FieldElement{-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771}, + }, + { + FieldElement{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817}, + FieldElement{10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098}, + FieldElement{10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409}, + }, + { + FieldElement{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504}, + FieldElement{-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727}, + FieldElement{28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420}, + }, + { + FieldElement{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003}, + FieldElement{-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605}, + FieldElement{-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384}, + }, + { + FieldElement{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701}, + FieldElement{-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683}, + FieldElement{29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708}, + }, + { + FieldElement{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563}, + FieldElement{-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260}, + FieldElement{-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387}, + }, + { + FieldElement{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672}, + FieldElement{23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686}, + FieldElement{-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665}, + }, + }, + { + { + FieldElement{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182}, + FieldElement{-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277}, + FieldElement{14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628}, + }, + { + FieldElement{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474}, + FieldElement{-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539}, + FieldElement{-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822}, + }, + { + FieldElement{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970}, + FieldElement{19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756}, + FieldElement{-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508}, + }, + { + FieldElement{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683}, + FieldElement{-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655}, + FieldElement{-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158}, + }, + { + FieldElement{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125}, + FieldElement{-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839}, + FieldElement{-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664}, + }, + { + FieldElement{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294}, + FieldElement{-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899}, + FieldElement{-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070}, + }, + { + FieldElement{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294}, + FieldElement{-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949}, + FieldElement{-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083}, + }, + { + FieldElement{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420}, + FieldElement{-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940}, + FieldElement{29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396}, + }, + }, + { + { + FieldElement{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567}, + FieldElement{20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127}, + FieldElement{-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294}, + }, + { + FieldElement{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887}, + FieldElement{22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964}, + FieldElement{16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195}, + }, + { + FieldElement{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244}, + FieldElement{24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999}, + FieldElement{-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762}, + }, + { + FieldElement{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274}, + FieldElement{-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236}, + FieldElement{-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605}, + }, + { + FieldElement{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761}, + FieldElement{-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884}, + FieldElement{-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482}, + }, + { + FieldElement{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638}, + FieldElement{-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490}, + FieldElement{-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170}, + }, + { + FieldElement{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736}, + FieldElement{10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124}, + FieldElement{-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392}, + }, + { + FieldElement{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029}, + FieldElement{6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048}, + FieldElement{28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958}, + }, + }, + { + { + FieldElement{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593}, + FieldElement{26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071}, + FieldElement{-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692}, + }, + { + FieldElement{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687}, + FieldElement{-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441}, + FieldElement{-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001}, + }, + { + FieldElement{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460}, + FieldElement{-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007}, + FieldElement{-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762}, + }, + { + FieldElement{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005}, + FieldElement{-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674}, + FieldElement{4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035}, + }, + { + FieldElement{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590}, + FieldElement{-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957}, + FieldElement{-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812}, + }, + { + FieldElement{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740}, + FieldElement{-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122}, + FieldElement{-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158}, + }, + { + FieldElement{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885}, + FieldElement{26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140}, + FieldElement{19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857}, + }, + { + FieldElement{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155}, + FieldElement{19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260}, + FieldElement{19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483}, + }, + }, + { + { + FieldElement{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677}, + FieldElement{32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815}, + FieldElement{22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751}, + }, + { + FieldElement{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203}, + FieldElement{-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208}, + FieldElement{1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230}, + }, + { + FieldElement{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850}, + FieldElement{-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389}, + FieldElement{-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968}, + }, + { + FieldElement{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689}, + FieldElement{14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880}, + FieldElement{5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304}, + }, + { + FieldElement{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632}, + FieldElement{-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412}, + FieldElement{20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566}, + }, + { + FieldElement{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038}, + FieldElement{-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232}, + FieldElement{-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943}, + }, + { + FieldElement{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856}, + FieldElement{23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738}, + FieldElement{15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971}, + }, + { + FieldElement{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718}, + FieldElement{-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697}, + FieldElement{-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883}, + }, + }, + { + { + FieldElement{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912}, + FieldElement{-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358}, + FieldElement{3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849}, + }, + { + FieldElement{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307}, + FieldElement{-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977}, + FieldElement{-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335}, + }, + { + FieldElement{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644}, + FieldElement{-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616}, + FieldElement{-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735}, + }, + { + FieldElement{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099}, + FieldElement{29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341}, + FieldElement{-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336}, + }, + { + FieldElement{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646}, + FieldElement{31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425}, + FieldElement{-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388}, + }, + { + FieldElement{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743}, + FieldElement{-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822}, + FieldElement{-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462}, + }, + { + FieldElement{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985}, + FieldElement{9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702}, + FieldElement{-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797}, + }, + { + FieldElement{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293}, + FieldElement{27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100}, + FieldElement{19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688}, + }, + }, + { + { + FieldElement{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186}, + FieldElement{2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610}, + FieldElement{-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707}, + }, + { + FieldElement{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220}, + FieldElement{915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025}, + FieldElement{32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044}, + }, + { + FieldElement{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992}, + FieldElement{-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027}, + FieldElement{21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197}, + }, + { + FieldElement{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901}, + FieldElement{31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952}, + FieldElement{19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878}, + }, + { + FieldElement{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390}, + FieldElement{32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730}, + FieldElement{2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730}, + }, + { + FieldElement{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180}, + FieldElement{-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272}, + FieldElement{-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715}, + }, + { + FieldElement{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970}, + FieldElement{-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772}, + FieldElement{-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865}, + }, + { + FieldElement{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750}, + FieldElement{20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373}, + FieldElement{32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348}, + }, + }, + { + { + FieldElement{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144}, + FieldElement{-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195}, + FieldElement{5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086}, + }, + { + FieldElement{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684}, + FieldElement{-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518}, + FieldElement{-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233}, + }, + { + FieldElement{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793}, + FieldElement{-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794}, + FieldElement{580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435}, + }, + { + FieldElement{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921}, + FieldElement{13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518}, + FieldElement{2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563}, + }, + { + FieldElement{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278}, + FieldElement{-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024}, + FieldElement{4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030}, + }, + { + FieldElement{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783}, + FieldElement{27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717}, + FieldElement{6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844}, + }, + { + FieldElement{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333}, + FieldElement{16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048}, + FieldElement{22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760}, + }, + { + FieldElement{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760}, + FieldElement{-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757}, + FieldElement{-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112}, + }, + }, + { + { + FieldElement{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468}, + FieldElement{3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184}, + FieldElement{10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289}, + }, + { + FieldElement{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066}, + FieldElement{24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882}, + FieldElement{13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226}, + }, + { + FieldElement{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101}, + FieldElement{29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279}, + FieldElement{-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811}, + }, + { + FieldElement{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709}, + FieldElement{20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714}, + FieldElement{-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121}, + }, + { + FieldElement{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464}, + FieldElement{12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847}, + FieldElement{13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400}, + }, + { + FieldElement{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414}, + FieldElement{-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158}, + FieldElement{17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045}, + }, + { + FieldElement{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415}, + FieldElement{-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459}, + FieldElement{-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079}, + }, + { + FieldElement{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412}, + FieldElement{-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743}, + FieldElement{-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836}, + }, + }, + { + { + FieldElement{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022}, + FieldElement{18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429}, + FieldElement{-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065}, + }, + { + FieldElement{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861}, + FieldElement{10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000}, + FieldElement{-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101}, + }, + { + FieldElement{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815}, + FieldElement{29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642}, + FieldElement{10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966}, + }, + { + FieldElement{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574}, + FieldElement{-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742}, + FieldElement{-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689}, + }, + { + FieldElement{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020}, + FieldElement{-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772}, + FieldElement{3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982}, + }, + { + FieldElement{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953}, + FieldElement{-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218}, + FieldElement{-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265}, + }, + { + FieldElement{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073}, + FieldElement{-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325}, + FieldElement{-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798}, + }, + { + FieldElement{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870}, + FieldElement{-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863}, + FieldElement{-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927}, + }, + }, + { + { + FieldElement{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267}, + FieldElement{-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663}, + FieldElement{22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862}, + }, + { + FieldElement{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673}, + FieldElement{15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943}, + FieldElement{15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020}, + }, + { + FieldElement{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238}, + FieldElement{11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064}, + FieldElement{14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795}, + }, + { + FieldElement{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052}, + FieldElement{-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904}, + FieldElement{29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531}, + }, + { + FieldElement{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979}, + FieldElement{-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841}, + FieldElement{10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431}, + }, + { + FieldElement{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324}, + FieldElement{-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940}, + FieldElement{10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320}, + }, + { + FieldElement{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184}, + FieldElement{14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114}, + FieldElement{30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878}, + }, + { + FieldElement{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784}, + FieldElement{-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091}, + FieldElement{-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585}, + }, + }, + { + { + FieldElement{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208}, + FieldElement{10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864}, + FieldElement{17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661}, + }, + { + FieldElement{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233}, + FieldElement{26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212}, + FieldElement{-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525}, + }, + { + FieldElement{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068}, + FieldElement{9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397}, + FieldElement{-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988}, + }, + { + FieldElement{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889}, + FieldElement{32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038}, + FieldElement{14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697}, + }, + { + FieldElement{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875}, + FieldElement{-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905}, + FieldElement{-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656}, + }, + { + FieldElement{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818}, + FieldElement{27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714}, + FieldElement{10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203}, + }, + { + FieldElement{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931}, + FieldElement{-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024}, + FieldElement{-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084}, + }, + { + FieldElement{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204}, + FieldElement{20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817}, + FieldElement{27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667}, + }, + }, + { + { + FieldElement{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504}, + FieldElement{-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768}, + FieldElement{-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255}, + }, + { + FieldElement{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790}, + FieldElement{1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438}, + FieldElement{-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333}, + }, + { + FieldElement{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971}, + FieldElement{31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905}, + FieldElement{29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409}, + }, + { + FieldElement{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409}, + FieldElement{6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499}, + FieldElement{-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363}, + }, + { + FieldElement{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664}, + FieldElement{-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324}, + FieldElement{-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940}, + }, + { + FieldElement{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990}, + FieldElement{-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914}, + FieldElement{-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290}, + }, + { + FieldElement{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257}, + FieldElement{-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433}, + FieldElement{-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236}, + }, + { + FieldElement{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045}, + FieldElement{11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093}, + FieldElement{-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347}, + }, + }, + { + { + FieldElement{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191}, + FieldElement{-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507}, + FieldElement{-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906}, + }, + { + FieldElement{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018}, + FieldElement{-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109}, + FieldElement{-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926}, + }, + { + FieldElement{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528}, + FieldElement{8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625}, + FieldElement{-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286}, + }, + { + FieldElement{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033}, + FieldElement{27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866}, + FieldElement{21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896}, + }, + { + FieldElement{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075}, + FieldElement{26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347}, + FieldElement{-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437}, + }, + { + FieldElement{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165}, + FieldElement{-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588}, + FieldElement{-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193}, + }, + { + FieldElement{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017}, + FieldElement{-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883}, + FieldElement{21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961}, + }, + { + FieldElement{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043}, + FieldElement{29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663}, + FieldElement{-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362}, + }, + }, + { + { + FieldElement{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860}, + FieldElement{2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466}, + FieldElement{-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063}, + }, + { + FieldElement{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997}, + FieldElement{-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295}, + FieldElement{-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369}, + }, + { + FieldElement{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385}, + FieldElement{18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109}, + FieldElement{2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906}, + }, + { + FieldElement{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424}, + FieldElement{-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185}, + FieldElement{7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962}, + }, + { + FieldElement{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325}, + FieldElement{10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593}, + FieldElement{696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404}, + }, + { + FieldElement{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644}, + FieldElement{17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801}, + FieldElement{26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804}, + }, + { + FieldElement{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884}, + FieldElement{-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577}, + FieldElement{-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849}, + }, + { + FieldElement{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473}, + FieldElement{-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644}, + FieldElement{-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319}, + }, + }, + { + { + FieldElement{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599}, + FieldElement{-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768}, + FieldElement{-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084}, + }, + { + FieldElement{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328}, + FieldElement{-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369}, + FieldElement{20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920}, + }, + { + FieldElement{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815}, + FieldElement{-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025}, + FieldElement{-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397}, + }, + { + FieldElement{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448}, + FieldElement{6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981}, + FieldElement{30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165}, + }, + { + FieldElement{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501}, + FieldElement{17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073}, + FieldElement{-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861}, + }, + { + FieldElement{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845}, + FieldElement{-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211}, + FieldElement{18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870}, + }, + { + FieldElement{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096}, + FieldElement{33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803}, + FieldElement{-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168}, + }, + { + FieldElement{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965}, + FieldElement{-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505}, + FieldElement{18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598}, + }, + }, + { + { + FieldElement{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782}, + FieldElement{5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900}, + FieldElement{-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479}, + }, + { + FieldElement{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208}, + FieldElement{8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232}, + FieldElement{17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719}, + }, + { + FieldElement{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271}, + FieldElement{-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326}, + FieldElement{-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132}, + }, + { + FieldElement{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300}, + FieldElement{8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570}, + FieldElement{15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670}, + }, + { + FieldElement{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994}, + FieldElement{-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913}, + FieldElement{31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317}, + }, + { + FieldElement{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730}, + FieldElement{842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096}, + FieldElement{-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078}, + }, + { + FieldElement{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411}, + FieldElement{-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905}, + FieldElement{-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654}, + }, + { + FieldElement{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870}, + FieldElement{-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498}, + FieldElement{12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579}, + }, + }, + { + { + FieldElement{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677}, + FieldElement{10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647}, + FieldElement{-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743}, + }, + { + FieldElement{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468}, + FieldElement{21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375}, + FieldElement{-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155}, + }, + { + FieldElement{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725}, + FieldElement{-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612}, + FieldElement{-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943}, + }, + { + FieldElement{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944}, + FieldElement{30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928}, + FieldElement{9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406}, + }, + { + FieldElement{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139}, + FieldElement{-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963}, + FieldElement{-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693}, + }, + { + FieldElement{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734}, + FieldElement{-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680}, + FieldElement{-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410}, + }, + { + FieldElement{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931}, + FieldElement{-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654}, + FieldElement{22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710}, + }, + { + FieldElement{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180}, + FieldElement{-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684}, + FieldElement{-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895}, + }, + }, + { + { + FieldElement{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501}, + FieldElement{-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413}, + FieldElement{6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880}, + }, + { + FieldElement{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874}, + FieldElement{22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962}, + FieldElement{-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899}, + }, + { + FieldElement{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152}, + FieldElement{9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063}, + FieldElement{7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080}, + }, + { + FieldElement{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146}, + FieldElement{-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183}, + FieldElement{-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133}, + }, + { + FieldElement{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421}, + FieldElement{-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622}, + FieldElement{-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197}, + }, + { + FieldElement{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663}, + FieldElement{31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753}, + FieldElement{4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755}, + }, + { + FieldElement{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862}, + FieldElement{-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118}, + FieldElement{26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171}, + }, + { + FieldElement{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380}, + FieldElement{16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824}, + FieldElement{28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270}, + }, + }, + { + { + FieldElement{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438}, + FieldElement{-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584}, + FieldElement{-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562}, + }, + { + FieldElement{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471}, + FieldElement{18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610}, + FieldElement{19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269}, + }, + { + FieldElement{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650}, + FieldElement{14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369}, + FieldElement{19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461}, + }, + { + FieldElement{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462}, + FieldElement{-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793}, + FieldElement{-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218}, + }, + { + FieldElement{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226}, + FieldElement{18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019}, + FieldElement{-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037}, + }, + { + FieldElement{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171}, + FieldElement{-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132}, + FieldElement{-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841}, + }, + { + FieldElement{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181}, + FieldElement{-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210}, + FieldElement{-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040}, + }, + { + FieldElement{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935}, + FieldElement{24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105}, + FieldElement{-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814}, + }, + }, + { + { + FieldElement{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852}, + FieldElement{5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581}, + FieldElement{-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646}, + }, + { + FieldElement{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844}, + FieldElement{10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025}, + FieldElement{27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453}, + }, + { + FieldElement{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068}, + FieldElement{4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192}, + FieldElement{-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921}, + }, + { + FieldElement{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259}, + FieldElement{-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426}, + FieldElement{-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072}, + }, + { + FieldElement{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305}, + FieldElement{13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832}, + FieldElement{28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943}, + }, + { + FieldElement{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011}, + FieldElement{24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447}, + FieldElement{17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494}, + }, + { + FieldElement{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245}, + FieldElement{-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859}, + FieldElement{28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915}, + }, + { + FieldElement{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707}, + FieldElement{10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848}, + FieldElement{-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224}, + }, + }, + { + { + FieldElement{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391}, + FieldElement{15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215}, + FieldElement{-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101}, + }, + { + FieldElement{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713}, + FieldElement{21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849}, + FieldElement{-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930}, + }, + { + FieldElement{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940}, + FieldElement{-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031}, + FieldElement{-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404}, + }, + { + FieldElement{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243}, + FieldElement{-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116}, + FieldElement{-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525}, + }, + { + FieldElement{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509}, + FieldElement{-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883}, + FieldElement{15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865}, + }, + { + FieldElement{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660}, + FieldElement{4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273}, + FieldElement{-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138}, + }, + { + FieldElement{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560}, + FieldElement{-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135}, + FieldElement{2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941}, + }, + { + FieldElement{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739}, + FieldElement{18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756}, + FieldElement{-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819}, + }, + }, + { + { + FieldElement{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347}, + FieldElement{-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028}, + FieldElement{21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075}, + }, + { + FieldElement{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799}, + FieldElement{-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609}, + FieldElement{-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817}, + }, + { + FieldElement{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989}, + FieldElement{-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523}, + FieldElement{4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278}, + }, + { + FieldElement{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045}, + FieldElement{19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377}, + FieldElement{24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480}, + }, + { + FieldElement{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016}, + FieldElement{510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426}, + FieldElement{18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525}, + }, + { + FieldElement{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396}, + FieldElement{9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080}, + FieldElement{12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892}, + }, + { + FieldElement{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275}, + FieldElement{11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074}, + FieldElement{20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140}, + }, + { + FieldElement{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717}, + FieldElement{-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101}, + FieldElement{24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127}, + }, + }, + { + { + FieldElement{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632}, + FieldElement{-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415}, + FieldElement{-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160}, + }, + { + FieldElement{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876}, + FieldElement{22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625}, + FieldElement{-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478}, + }, + { + FieldElement{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164}, + FieldElement{26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595}, + FieldElement{-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248}, + }, + { + FieldElement{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858}, + FieldElement{15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193}, + FieldElement{8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184}, + }, + { + FieldElement{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942}, + FieldElement{-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635}, + FieldElement{21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948}, + }, + { + FieldElement{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935}, + FieldElement{-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415}, + FieldElement{-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416}, + }, + { + FieldElement{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018}, + FieldElement{4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778}, + FieldElement{366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659}, + }, + { + FieldElement{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385}, + FieldElement{18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503}, + FieldElement{476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329}, + }, + }, + { + { + FieldElement{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056}, + FieldElement{-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838}, + FieldElement{24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948}, + }, + { + FieldElement{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691}, + FieldElement{-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118}, + FieldElement{-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517}, + }, + { + FieldElement{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269}, + FieldElement{-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904}, + FieldElement{-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589}, + }, + { + FieldElement{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193}, + FieldElement{-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910}, + FieldElement{-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930}, + }, + { + FieldElement{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667}, + FieldElement{25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481}, + FieldElement{-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876}, + }, + { + FieldElement{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640}, + FieldElement{-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278}, + FieldElement{-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112}, + }, + { + FieldElement{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272}, + FieldElement{17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012}, + FieldElement{-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221}, + }, + { + FieldElement{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046}, + FieldElement{13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345}, + FieldElement{-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310}, + }, + }, + { + { + FieldElement{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937}, + FieldElement{31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636}, + FieldElement{-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008}, + }, + { + FieldElement{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429}, + FieldElement{-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576}, + FieldElement{31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066}, + }, + { + FieldElement{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490}, + FieldElement{-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104}, + FieldElement{33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053}, + }, + { + FieldElement{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275}, + FieldElement{-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511}, + FieldElement{22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095}, + }, + { + FieldElement{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439}, + FieldElement{23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939}, + FieldElement{-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424}, + }, + { + FieldElement{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310}, + FieldElement{3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608}, + FieldElement{-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079}, + }, + { + FieldElement{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101}, + FieldElement{21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418}, + FieldElement{18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576}, + }, + { + FieldElement{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356}, + FieldElement{9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996}, + FieldElement{-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099}, + }, + }, + { + { + FieldElement{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728}, + FieldElement{-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658}, + FieldElement{-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242}, + }, + { + FieldElement{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001}, + FieldElement{-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766}, + FieldElement{18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373}, + }, + { + FieldElement{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458}, + FieldElement{-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628}, + FieldElement{-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657}, + }, + { + FieldElement{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062}, + FieldElement{25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616}, + FieldElement{31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014}, + }, + { + FieldElement{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383}, + FieldElement{-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814}, + FieldElement{-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718}, + }, + { + FieldElement{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417}, + FieldElement{2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222}, + FieldElement{33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444}, + }, + { + FieldElement{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597}, + FieldElement{23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970}, + FieldElement{1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799}, + }, + { + FieldElement{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647}, + FieldElement{13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511}, + FieldElement{-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032}, + }, + }, + { + { + FieldElement{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834}, + FieldElement{-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461}, + FieldElement{29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062}, + }, + { + FieldElement{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516}, + FieldElement{-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547}, + FieldElement{-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240}, + }, + { + FieldElement{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038}, + FieldElement{-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741}, + FieldElement{16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103}, + }, + { + FieldElement{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747}, + FieldElement{-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323}, + FieldElement{31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016}, + }, + { + FieldElement{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373}, + FieldElement{15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228}, + FieldElement{-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141}, + }, + { + FieldElement{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399}, + FieldElement{11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831}, + FieldElement{-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376}, + }, + { + FieldElement{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313}, + FieldElement{-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958}, + FieldElement{-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577}, + }, + { + FieldElement{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743}, + FieldElement{29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684}, + FieldElement{-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476}, + }, + }, +} diff --git a/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go new file mode 100644 index 000000000..5f8b99478 --- /dev/null +++ b/vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go @@ -0,0 +1,1771 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package edwards25519 + +// This code is a port of the public domain, “ref10” implementation of ed25519 +// from SUPERCOP. + +// FieldElement represents an element of the field GF(2^255 - 19). An element +// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 +// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on +// context. +type FieldElement [10]int32 + +var zero FieldElement + +func FeZero(fe *FieldElement) { + copy(fe[:], zero[:]) +} + +func FeOne(fe *FieldElement) { + FeZero(fe) + fe[0] = 1 +} + +func FeAdd(dst, a, b *FieldElement) { + dst[0] = a[0] + b[0] + dst[1] = a[1] + b[1] + dst[2] = a[2] + b[2] + dst[3] = a[3] + b[3] + dst[4] = a[4] + b[4] + dst[5] = a[5] + b[5] + dst[6] = a[6] + b[6] + dst[7] = a[7] + b[7] + dst[8] = a[8] + b[8] + dst[9] = a[9] + b[9] +} + +func FeSub(dst, a, b *FieldElement) { + dst[0] = a[0] - b[0] + dst[1] = a[1] - b[1] + dst[2] = a[2] - b[2] + dst[3] = a[3] - b[3] + dst[4] = a[4] - b[4] + dst[5] = a[5] - b[5] + dst[6] = a[6] - b[6] + dst[7] = a[7] - b[7] + dst[8] = a[8] - b[8] + dst[9] = a[9] - b[9] +} + +func FeCopy(dst, src *FieldElement) { + copy(dst[:], src[:]) +} + +// Replace (f,g) with (g,g) if b == 1; +// replace (f,g) with (f,g) if b == 0. +// +// Preconditions: b in {0,1}. +func FeCMove(f, g *FieldElement, b int32) { + b = -b + f[0] ^= b & (f[0] ^ g[0]) + f[1] ^= b & (f[1] ^ g[1]) + f[2] ^= b & (f[2] ^ g[2]) + f[3] ^= b & (f[3] ^ g[3]) + f[4] ^= b & (f[4] ^ g[4]) + f[5] ^= b & (f[5] ^ g[5]) + f[6] ^= b & (f[6] ^ g[6]) + f[7] ^= b & (f[7] ^ g[7]) + f[8] ^= b & (f[8] ^ g[8]) + f[9] ^= b & (f[9] ^ g[9]) +} + +func load3(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + return r +} + +func load4(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + r |= int64(in[3]) << 24 + return r +} + +func FeFromBytes(dst *FieldElement, src *[32]byte) { + h0 := load4(src[:]) + h1 := load3(src[4:]) << 6 + h2 := load3(src[7:]) << 5 + h3 := load3(src[10:]) << 3 + h4 := load3(src[13:]) << 2 + h5 := load4(src[16:]) + h6 := load3(src[20:]) << 7 + h7 := load3(src[23:]) << 5 + h8 := load3(src[26:]) << 4 + h9 := (load3(src[29:]) & 8388607) << 2 + + FeCombine(dst, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) +} + +// FeToBytes marshals h to s. +// Preconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Write p=2^255-19; q=floor(h/p). +// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). +// +// Proof: +// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. +// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. +// +// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). +// Then 0<y<1. +// +// Write r=h-pq. +// Have 0<=r<=p-1=2^255-20. +// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1. +// +// Write x=r+19(2^-255)r+y. +// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q. +// +// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1)) +// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. +func FeToBytes(s *[32]byte, h *FieldElement) { + var carry [10]int32 + + q := (19*h[9] + (1 << 24)) >> 25 + q = (h[0] + q) >> 26 + q = (h[1] + q) >> 25 + q = (h[2] + q) >> 26 + q = (h[3] + q) >> 25 + q = (h[4] + q) >> 26 + q = (h[5] + q) >> 25 + q = (h[6] + q) >> 26 + q = (h[7] + q) >> 25 + q = (h[8] + q) >> 26 + q = (h[9] + q) >> 25 + + // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. + h[0] += 19 * q + // Goal: Output h-2^255 q, which is between 0 and 2^255-20. + + carry[0] = h[0] >> 26 + h[1] += carry[0] + h[0] -= carry[0] << 26 + carry[1] = h[1] >> 25 + h[2] += carry[1] + h[1] -= carry[1] << 25 + carry[2] = h[2] >> 26 + h[3] += carry[2] + h[2] -= carry[2] << 26 + carry[3] = h[3] >> 25 + h[4] += carry[3] + h[3] -= carry[3] << 25 + carry[4] = h[4] >> 26 + h[5] += carry[4] + h[4] -= carry[4] << 26 + carry[5] = h[5] >> 25 + h[6] += carry[5] + h[5] -= carry[5] << 25 + carry[6] = h[6] >> 26 + h[7] += carry[6] + h[6] -= carry[6] << 26 + carry[7] = h[7] >> 25 + h[8] += carry[7] + h[7] -= carry[7] << 25 + carry[8] = h[8] >> 26 + h[9] += carry[8] + h[8] -= carry[8] << 26 + carry[9] = h[9] >> 25 + h[9] -= carry[9] << 25 + // h10 = carry9 + + // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; + // evidently 2^255 h10-2^255 q = 0. + // Goal: Output h[0]+...+2^230 h[9]. + + s[0] = byte(h[0] >> 0) + s[1] = byte(h[0] >> 8) + s[2] = byte(h[0] >> 16) + s[3] = byte((h[0] >> 24) | (h[1] << 2)) + s[4] = byte(h[1] >> 6) + s[5] = byte(h[1] >> 14) + s[6] = byte((h[1] >> 22) | (h[2] << 3)) + s[7] = byte(h[2] >> 5) + s[8] = byte(h[2] >> 13) + s[9] = byte((h[2] >> 21) | (h[3] << 5)) + s[10] = byte(h[3] >> 3) + s[11] = byte(h[3] >> 11) + s[12] = byte((h[3] >> 19) | (h[4] << 6)) + s[13] = byte(h[4] >> 2) + s[14] = byte(h[4] >> 10) + s[15] = byte(h[4] >> 18) + s[16] = byte(h[5] >> 0) + s[17] = byte(h[5] >> 8) + s[18] = byte(h[5] >> 16) + s[19] = byte((h[5] >> 24) | (h[6] << 1)) + s[20] = byte(h[6] >> 7) + s[21] = byte(h[6] >> 15) + s[22] = byte((h[6] >> 23) | (h[7] << 3)) + s[23] = byte(h[7] >> 5) + s[24] = byte(h[7] >> 13) + s[25] = byte((h[7] >> 21) | (h[8] << 4)) + s[26] = byte(h[8] >> 4) + s[27] = byte(h[8] >> 12) + s[28] = byte((h[8] >> 20) | (h[9] << 6)) + s[29] = byte(h[9] >> 2) + s[30] = byte(h[9] >> 10) + s[31] = byte(h[9] >> 18) +} + +func FeIsNegative(f *FieldElement) byte { + var s [32]byte + FeToBytes(&s, f) + return s[0] & 1 +} + +func FeIsNonZero(f *FieldElement) int32 { + var s [32]byte + FeToBytes(&s, f) + var x uint8 + for _, b := range s { + x |= b + } + x |= x >> 4 + x |= x >> 2 + x |= x >> 1 + return int32(x & 1) +} + +// FeNeg sets h = -f +// +// Preconditions: +// |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func FeNeg(h, f *FieldElement) { + h[0] = -f[0] + h[1] = -f[1] + h[2] = -f[2] + h[3] = -f[3] + h[4] = -f[4] + h[5] = -f[5] + h[6] = -f[6] + h[7] = -f[7] + h[8] = -f[8] + h[9] = -f[9] +} + +func FeCombine(h *FieldElement, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) { + var c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 int64 + + /* + |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) + i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 + |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) + i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 + */ + + c0 = (h0 + (1 << 25)) >> 26 + h1 += c0 + h0 -= c0 << 26 + c4 = (h4 + (1 << 25)) >> 26 + h5 += c4 + h4 -= c4 << 26 + /* |h0| <= 2^25 */ + /* |h4| <= 2^25 */ + /* |h1| <= 1.51*2^58 */ + /* |h5| <= 1.51*2^58 */ + + c1 = (h1 + (1 << 24)) >> 25 + h2 += c1 + h1 -= c1 << 25 + c5 = (h5 + (1 << 24)) >> 25 + h6 += c5 + h5 -= c5 << 25 + /* |h1| <= 2^24; from now on fits into int32 */ + /* |h5| <= 2^24; from now on fits into int32 */ + /* |h2| <= 1.21*2^59 */ + /* |h6| <= 1.21*2^59 */ + + c2 = (h2 + (1 << 25)) >> 26 + h3 += c2 + h2 -= c2 << 26 + c6 = (h6 + (1 << 25)) >> 26 + h7 += c6 + h6 -= c6 << 26 + /* |h2| <= 2^25; from now on fits into int32 unchanged */ + /* |h6| <= 2^25; from now on fits into int32 unchanged */ + /* |h3| <= 1.51*2^58 */ + /* |h7| <= 1.51*2^58 */ + + c3 = (h3 + (1 << 24)) >> 25 + h4 += c3 + h3 -= c3 << 25 + c7 = (h7 + (1 << 24)) >> 25 + h8 += c7 + h7 -= c7 << 25 + /* |h3| <= 2^24; from now on fits into int32 unchanged */ + /* |h7| <= 2^24; from now on fits into int32 unchanged */ + /* |h4| <= 1.52*2^33 */ + /* |h8| <= 1.52*2^33 */ + + c4 = (h4 + (1 << 25)) >> 26 + h5 += c4 + h4 -= c4 << 26 + c8 = (h8 + (1 << 25)) >> 26 + h9 += c8 + h8 -= c8 << 26 + /* |h4| <= 2^25; from now on fits into int32 unchanged */ + /* |h8| <= 2^25; from now on fits into int32 unchanged */ + /* |h5| <= 1.01*2^24 */ + /* |h9| <= 1.51*2^58 */ + + c9 = (h9 + (1 << 24)) >> 25 + h0 += c9 * 19 + h9 -= c9 << 25 + /* |h9| <= 2^24; from now on fits into int32 unchanged */ + /* |h0| <= 1.8*2^37 */ + + c0 = (h0 + (1 << 25)) >> 26 + h1 += c0 + h0 -= c0 << 26 + /* |h0| <= 2^25; from now on fits into int32 unchanged */ + /* |h1| <= 1.01*2^24 */ + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// FeMul calculates h = f * g +// Can overlap h with f or g. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Notes on implementation strategy: +// +// Using schoolbook multiplication. +// Karatsuba would save a little in some cost models. +// +// Most multiplications by 2 and 19 are 32-bit precomputations; +// cheaper than 64-bit postcomputations. +// +// There is one remaining multiplication by 19 in the carry chain; +// one *19 precomputation can be merged into this, +// but the resulting data flow is considerably less clean. +// +// There are 12 carries below. +// 10 of them are 2-way parallelizable and vectorizable. +// Can get away with 11 carries, but then data flow is much deeper. +// +// With tighter constraints on inputs, can squeeze carries into int32. +func FeMul(h, f, g *FieldElement) { + f0 := int64(f[0]) + f1 := int64(f[1]) + f2 := int64(f[2]) + f3 := int64(f[3]) + f4 := int64(f[4]) + f5 := int64(f[5]) + f6 := int64(f[6]) + f7 := int64(f[7]) + f8 := int64(f[8]) + f9 := int64(f[9]) + + f1_2 := int64(2 * f[1]) + f3_2 := int64(2 * f[3]) + f5_2 := int64(2 * f[5]) + f7_2 := int64(2 * f[7]) + f9_2 := int64(2 * f[9]) + + g0 := int64(g[0]) + g1 := int64(g[1]) + g2 := int64(g[2]) + g3 := int64(g[3]) + g4 := int64(g[4]) + g5 := int64(g[5]) + g6 := int64(g[6]) + g7 := int64(g[7]) + g8 := int64(g[8]) + g9 := int64(g[9]) + + g1_19 := int64(19 * g[1]) /* 1.4*2^29 */ + g2_19 := int64(19 * g[2]) /* 1.4*2^30; still ok */ + g3_19 := int64(19 * g[3]) + g4_19 := int64(19 * g[4]) + g5_19 := int64(19 * g[5]) + g6_19 := int64(19 * g[6]) + g7_19 := int64(19 * g[7]) + g8_19 := int64(19 * g[8]) + g9_19 := int64(19 * g[9]) + + h0 := f0*g0 + f1_2*g9_19 + f2*g8_19 + f3_2*g7_19 + f4*g6_19 + f5_2*g5_19 + f6*g4_19 + f7_2*g3_19 + f8*g2_19 + f9_2*g1_19 + h1 := f0*g1 + f1*g0 + f2*g9_19 + f3*g8_19 + f4*g7_19 + f5*g6_19 + f6*g5_19 + f7*g4_19 + f8*g3_19 + f9*g2_19 + h2 := f0*g2 + f1_2*g1 + f2*g0 + f3_2*g9_19 + f4*g8_19 + f5_2*g7_19 + f6*g6_19 + f7_2*g5_19 + f8*g4_19 + f9_2*g3_19 + h3 := f0*g3 + f1*g2 + f2*g1 + f3*g0 + f4*g9_19 + f5*g8_19 + f6*g7_19 + f7*g6_19 + f8*g5_19 + f9*g4_19 + h4 := f0*g4 + f1_2*g3 + f2*g2 + f3_2*g1 + f4*g0 + f5_2*g9_19 + f6*g8_19 + f7_2*g7_19 + f8*g6_19 + f9_2*g5_19 + h5 := f0*g5 + f1*g4 + f2*g3 + f3*g2 + f4*g1 + f5*g0 + f6*g9_19 + f7*g8_19 + f8*g7_19 + f9*g6_19 + h6 := f0*g6 + f1_2*g5 + f2*g4 + f3_2*g3 + f4*g2 + f5_2*g1 + f6*g0 + f7_2*g9_19 + f8*g8_19 + f9_2*g7_19 + h7 := f0*g7 + f1*g6 + f2*g5 + f3*g4 + f4*g3 + f5*g2 + f6*g1 + f7*g0 + f8*g9_19 + f9*g8_19 + h8 := f0*g8 + f1_2*g7 + f2*g6 + f3_2*g5 + f4*g4 + f5_2*g3 + f6*g2 + f7_2*g1 + f8*g0 + f9_2*g9_19 + h9 := f0*g9 + f1*g8 + f2*g7 + f3*g6 + f4*g5 + f5*g4 + f6*g3 + f7*g2 + f8*g1 + f9*g0 + + FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) +} + +func feSquare(f *FieldElement) (h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) { + f0 := int64(f[0]) + f1 := int64(f[1]) + f2 := int64(f[2]) + f3 := int64(f[3]) + f4 := int64(f[4]) + f5 := int64(f[5]) + f6 := int64(f[6]) + f7 := int64(f[7]) + f8 := int64(f[8]) + f9 := int64(f[9]) + f0_2 := int64(2 * f[0]) + f1_2 := int64(2 * f[1]) + f2_2 := int64(2 * f[2]) + f3_2 := int64(2 * f[3]) + f4_2 := int64(2 * f[4]) + f5_2 := int64(2 * f[5]) + f6_2 := int64(2 * f[6]) + f7_2 := int64(2 * f[7]) + f5_38 := 38 * f5 // 1.31*2^30 + f6_19 := 19 * f6 // 1.31*2^30 + f7_38 := 38 * f7 // 1.31*2^30 + f8_19 := 19 * f8 // 1.31*2^30 + f9_38 := 38 * f9 // 1.31*2^30 + + h0 = f0*f0 + f1_2*f9_38 + f2_2*f8_19 + f3_2*f7_38 + f4_2*f6_19 + f5*f5_38 + h1 = f0_2*f1 + f2*f9_38 + f3_2*f8_19 + f4*f7_38 + f5_2*f6_19 + h2 = f0_2*f2 + f1_2*f1 + f3_2*f9_38 + f4_2*f8_19 + f5_2*f7_38 + f6*f6_19 + h3 = f0_2*f3 + f1_2*f2 + f4*f9_38 + f5_2*f8_19 + f6*f7_38 + h4 = f0_2*f4 + f1_2*f3_2 + f2*f2 + f5_2*f9_38 + f6_2*f8_19 + f7*f7_38 + h5 = f0_2*f5 + f1_2*f4 + f2_2*f3 + f6*f9_38 + f7_2*f8_19 + h6 = f0_2*f6 + f1_2*f5_2 + f2_2*f4 + f3_2*f3 + f7_2*f9_38 + f8*f8_19 + h7 = f0_2*f7 + f1_2*f6 + f2_2*f5 + f3_2*f4 + f8*f9_38 + h8 = f0_2*f8 + f1_2*f7_2 + f2_2*f6 + f3_2*f5_2 + f4*f4 + f9*f9_38 + h9 = f0_2*f9 + f1_2*f8 + f2_2*f7 + f3_2*f6 + f4_2*f5 + + return +} + +// FeSquare calculates h = f*f. Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func FeSquare(h, f *FieldElement) { + h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f) + FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) +} + +// FeSquare2 sets h = 2 * f * f +// +// Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +// See fe_mul.c for discussion of implementation strategy. +func FeSquare2(h, f *FieldElement) { + h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f) + + h0 += h0 + h1 += h1 + h2 += h2 + h3 += h3 + h4 += h4 + h5 += h5 + h6 += h6 + h7 += h7 + h8 += h8 + h9 += h9 + + FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) +} + +func FeInvert(out, z *FieldElement) { + var t0, t1, t2, t3 FieldElement + var i int + + FeSquare(&t0, z) // 2^1 + FeSquare(&t1, &t0) // 2^2 + for i = 1; i < 2; i++ { // 2^3 + FeSquare(&t1, &t1) + } + FeMul(&t1, z, &t1) // 2^3 + 2^0 + FeMul(&t0, &t0, &t1) // 2^3 + 2^1 + 2^0 + FeSquare(&t2, &t0) // 2^4 + 2^2 + 2^1 + FeMul(&t1, &t1, &t2) // 2^4 + 2^3 + 2^2 + 2^1 + 2^0 + FeSquare(&t2, &t1) // 5,4,3,2,1 + for i = 1; i < 5; i++ { // 9,8,7,6,5 + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0 + FeSquare(&t2, &t1) // 10..1 + for i = 1; i < 10; i++ { // 19..10 + FeSquare(&t2, &t2) + } + FeMul(&t2, &t2, &t1) // 19..0 + FeSquare(&t3, &t2) // 20..1 + for i = 1; i < 20; i++ { // 39..20 + FeSquare(&t3, &t3) + } + FeMul(&t2, &t3, &t2) // 39..0 + FeSquare(&t2, &t2) // 40..1 + for i = 1; i < 10; i++ { // 49..10 + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) // 49..0 + FeSquare(&t2, &t1) // 50..1 + for i = 1; i < 50; i++ { // 99..50 + FeSquare(&t2, &t2) + } + FeMul(&t2, &t2, &t1) // 99..0 + FeSquare(&t3, &t2) // 100..1 + for i = 1; i < 100; i++ { // 199..100 + FeSquare(&t3, &t3) + } + FeMul(&t2, &t3, &t2) // 199..0 + FeSquare(&t2, &t2) // 200..1 + for i = 1; i < 50; i++ { // 249..50 + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) // 249..0 + FeSquare(&t1, &t1) // 250..1 + for i = 1; i < 5; i++ { // 254..5 + FeSquare(&t1, &t1) + } + FeMul(out, &t1, &t0) // 254..5,3,1,0 +} + +func fePow22523(out, z *FieldElement) { + var t0, t1, t2 FieldElement + var i int + + FeSquare(&t0, z) + for i = 1; i < 1; i++ { + FeSquare(&t0, &t0) + } + FeSquare(&t1, &t0) + for i = 1; i < 2; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t1, z, &t1) + FeMul(&t0, &t0, &t1) + FeSquare(&t0, &t0) + for i = 1; i < 1; i++ { + FeSquare(&t0, &t0) + } + FeMul(&t0, &t1, &t0) + FeSquare(&t1, &t0) + for i = 1; i < 5; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t0, &t1, &t0) + FeSquare(&t1, &t0) + for i = 1; i < 10; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t1, &t1, &t0) + FeSquare(&t2, &t1) + for i = 1; i < 20; i++ { + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) + FeSquare(&t1, &t1) + for i = 1; i < 10; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t0, &t1, &t0) + FeSquare(&t1, &t0) + for i = 1; i < 50; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t1, &t1, &t0) + FeSquare(&t2, &t1) + for i = 1; i < 100; i++ { + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) + FeSquare(&t1, &t1) + for i = 1; i < 50; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t0, &t1, &t0) + FeSquare(&t0, &t0) + for i = 1; i < 2; i++ { + FeSquare(&t0, &t0) + } + FeMul(out, &t0, z) +} + +// Group elements are members of the elliptic curve -x^2 + y^2 = 1 + d * x^2 * +// y^2 where d = -121665/121666. +// +// Several representations are used: +// ProjectiveGroupElement: (X:Y:Z) satisfying x=X/Z, y=Y/Z +// ExtendedGroupElement: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT +// CompletedGroupElement: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T +// PreComputedGroupElement: (y+x,y-x,2dxy) + +type ProjectiveGroupElement struct { + X, Y, Z FieldElement +} + +type ExtendedGroupElement struct { + X, Y, Z, T FieldElement +} + +type CompletedGroupElement struct { + X, Y, Z, T FieldElement +} + +type PreComputedGroupElement struct { + yPlusX, yMinusX, xy2d FieldElement +} + +type CachedGroupElement struct { + yPlusX, yMinusX, Z, T2d FieldElement +} + +func (p *ProjectiveGroupElement) Zero() { + FeZero(&p.X) + FeOne(&p.Y) + FeOne(&p.Z) +} + +func (p *ProjectiveGroupElement) Double(r *CompletedGroupElement) { + var t0 FieldElement + + FeSquare(&r.X, &p.X) + FeSquare(&r.Z, &p.Y) + FeSquare2(&r.T, &p.Z) + FeAdd(&r.Y, &p.X, &p.Y) + FeSquare(&t0, &r.Y) + FeAdd(&r.Y, &r.Z, &r.X) + FeSub(&r.Z, &r.Z, &r.X) + FeSub(&r.X, &t0, &r.Y) + FeSub(&r.T, &r.T, &r.Z) +} + +func (p *ProjectiveGroupElement) ToBytes(s *[32]byte) { + var recip, x, y FieldElement + + FeInvert(&recip, &p.Z) + FeMul(&x, &p.X, &recip) + FeMul(&y, &p.Y, &recip) + FeToBytes(s, &y) + s[31] ^= FeIsNegative(&x) << 7 +} + +func (p *ExtendedGroupElement) Zero() { + FeZero(&p.X) + FeOne(&p.Y) + FeOne(&p.Z) + FeZero(&p.T) +} + +func (p *ExtendedGroupElement) Double(r *CompletedGroupElement) { + var q ProjectiveGroupElement + p.ToProjective(&q) + q.Double(r) +} + +func (p *ExtendedGroupElement) ToCached(r *CachedGroupElement) { + FeAdd(&r.yPlusX, &p.Y, &p.X) + FeSub(&r.yMinusX, &p.Y, &p.X) + FeCopy(&r.Z, &p.Z) + FeMul(&r.T2d, &p.T, &d2) +} + +func (p *ExtendedGroupElement) ToProjective(r *ProjectiveGroupElement) { + FeCopy(&r.X, &p.X) + FeCopy(&r.Y, &p.Y) + FeCopy(&r.Z, &p.Z) +} + +func (p *ExtendedGroupElement) ToBytes(s *[32]byte) { + var recip, x, y FieldElement + + FeInvert(&recip, &p.Z) + FeMul(&x, &p.X, &recip) + FeMul(&y, &p.Y, &recip) + FeToBytes(s, &y) + s[31] ^= FeIsNegative(&x) << 7 +} + +func (p *ExtendedGroupElement) FromBytes(s *[32]byte) bool { + var u, v, v3, vxx, check FieldElement + + FeFromBytes(&p.Y, s) + FeOne(&p.Z) + FeSquare(&u, &p.Y) + FeMul(&v, &u, &d) + FeSub(&u, &u, &p.Z) // y = y^2-1 + FeAdd(&v, &v, &p.Z) // v = dy^2+1 + + FeSquare(&v3, &v) + FeMul(&v3, &v3, &v) // v3 = v^3 + FeSquare(&p.X, &v3) + FeMul(&p.X, &p.X, &v) + FeMul(&p.X, &p.X, &u) // x = uv^7 + + fePow22523(&p.X, &p.X) // x = (uv^7)^((q-5)/8) + FeMul(&p.X, &p.X, &v3) + FeMul(&p.X, &p.X, &u) // x = uv^3(uv^7)^((q-5)/8) + + var tmpX, tmp2 [32]byte + + FeSquare(&vxx, &p.X) + FeMul(&vxx, &vxx, &v) + FeSub(&check, &vxx, &u) // vx^2-u + if FeIsNonZero(&check) == 1 { + FeAdd(&check, &vxx, &u) // vx^2+u + if FeIsNonZero(&check) == 1 { + return false + } + FeMul(&p.X, &p.X, &SqrtM1) + + FeToBytes(&tmpX, &p.X) + for i, v := range tmpX { + tmp2[31-i] = v + } + } + + if FeIsNegative(&p.X) != (s[31] >> 7) { + FeNeg(&p.X, &p.X) + } + + FeMul(&p.T, &p.X, &p.Y) + return true +} + +func (p *CompletedGroupElement) ToProjective(r *ProjectiveGroupElement) { + FeMul(&r.X, &p.X, &p.T) + FeMul(&r.Y, &p.Y, &p.Z) + FeMul(&r.Z, &p.Z, &p.T) +} + +func (p *CompletedGroupElement) ToExtended(r *ExtendedGroupElement) { + FeMul(&r.X, &p.X, &p.T) + FeMul(&r.Y, &p.Y, &p.Z) + FeMul(&r.Z, &p.Z, &p.T) + FeMul(&r.T, &p.X, &p.Y) +} + +func (p *PreComputedGroupElement) Zero() { + FeOne(&p.yPlusX) + FeOne(&p.yMinusX) + FeZero(&p.xy2d) +} + +func geAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) { + var t0 FieldElement + + FeAdd(&r.X, &p.Y, &p.X) + FeSub(&r.Y, &p.Y, &p.X) + FeMul(&r.Z, &r.X, &q.yPlusX) + FeMul(&r.Y, &r.Y, &q.yMinusX) + FeMul(&r.T, &q.T2d, &p.T) + FeMul(&r.X, &p.Z, &q.Z) + FeAdd(&t0, &r.X, &r.X) + FeSub(&r.X, &r.Z, &r.Y) + FeAdd(&r.Y, &r.Z, &r.Y) + FeAdd(&r.Z, &t0, &r.T) + FeSub(&r.T, &t0, &r.T) +} + +func geSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) { + var t0 FieldElement + + FeAdd(&r.X, &p.Y, &p.X) + FeSub(&r.Y, &p.Y, &p.X) + FeMul(&r.Z, &r.X, &q.yMinusX) + FeMul(&r.Y, &r.Y, &q.yPlusX) + FeMul(&r.T, &q.T2d, &p.T) + FeMul(&r.X, &p.Z, &q.Z) + FeAdd(&t0, &r.X, &r.X) + FeSub(&r.X, &r.Z, &r.Y) + FeAdd(&r.Y, &r.Z, &r.Y) + FeSub(&r.Z, &t0, &r.T) + FeAdd(&r.T, &t0, &r.T) +} + +func geMixedAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) { + var t0 FieldElement + + FeAdd(&r.X, &p.Y, &p.X) + FeSub(&r.Y, &p.Y, &p.X) + FeMul(&r.Z, &r.X, &q.yPlusX) + FeMul(&r.Y, &r.Y, &q.yMinusX) + FeMul(&r.T, &q.xy2d, &p.T) + FeAdd(&t0, &p.Z, &p.Z) + FeSub(&r.X, &r.Z, &r.Y) + FeAdd(&r.Y, &r.Z, &r.Y) + FeAdd(&r.Z, &t0, &r.T) + FeSub(&r.T, &t0, &r.T) +} + +func geMixedSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) { + var t0 FieldElement + + FeAdd(&r.X, &p.Y, &p.X) + FeSub(&r.Y, &p.Y, &p.X) + FeMul(&r.Z, &r.X, &q.yMinusX) + FeMul(&r.Y, &r.Y, &q.yPlusX) + FeMul(&r.T, &q.xy2d, &p.T) + FeAdd(&t0, &p.Z, &p.Z) + FeSub(&r.X, &r.Z, &r.Y) + FeAdd(&r.Y, &r.Z, &r.Y) + FeSub(&r.Z, &t0, &r.T) + FeAdd(&r.T, &t0, &r.T) +} + +func slide(r *[256]int8, a *[32]byte) { + for i := range r { + r[i] = int8(1 & (a[i>>3] >> uint(i&7))) + } + + for i := range r { + if r[i] != 0 { + for b := 1; b <= 6 && i+b < 256; b++ { + if r[i+b] != 0 { + if r[i]+(r[i+b]<<uint(b)) <= 15 { + r[i] += r[i+b] << uint(b) + r[i+b] = 0 + } else if r[i]-(r[i+b]<<uint(b)) >= -15 { + r[i] -= r[i+b] << uint(b) + for k := i + b; k < 256; k++ { + if r[k] == 0 { + r[k] = 1 + break + } + r[k] = 0 + } + } else { + break + } + } + } + } + } +} + +// GeDoubleScalarMultVartime sets r = a*A + b*B +// where a = a[0]+256*a[1]+...+256^31 a[31]. +// and b = b[0]+256*b[1]+...+256^31 b[31]. +// B is the Ed25519 base point (x,4/5) with x positive. +func GeDoubleScalarMultVartime(r *ProjectiveGroupElement, a *[32]byte, A *ExtendedGroupElement, b *[32]byte) { + var aSlide, bSlide [256]int8 + var Ai [8]CachedGroupElement // A,3A,5A,7A,9A,11A,13A,15A + var t CompletedGroupElement + var u, A2 ExtendedGroupElement + var i int + + slide(&aSlide, a) + slide(&bSlide, b) + + A.ToCached(&Ai[0]) + A.Double(&t) + t.ToExtended(&A2) + + for i := 0; i < 7; i++ { + geAdd(&t, &A2, &Ai[i]) + t.ToExtended(&u) + u.ToCached(&Ai[i+1]) + } + + r.Zero() + + for i = 255; i >= 0; i-- { + if aSlide[i] != 0 || bSlide[i] != 0 { + break + } + } + + for ; i >= 0; i-- { + r.Double(&t) + + if aSlide[i] > 0 { + t.ToExtended(&u) + geAdd(&t, &u, &Ai[aSlide[i]/2]) + } else if aSlide[i] < 0 { + t.ToExtended(&u) + geSub(&t, &u, &Ai[(-aSlide[i])/2]) + } + + if bSlide[i] > 0 { + t.ToExtended(&u) + geMixedAdd(&t, &u, &bi[bSlide[i]/2]) + } else if bSlide[i] < 0 { + t.ToExtended(&u) + geMixedSub(&t, &u, &bi[(-bSlide[i])/2]) + } + + t.ToProjective(r) + } +} + +// equal returns 1 if b == c and 0 otherwise, assuming that b and c are +// non-negative. +func equal(b, c int32) int32 { + x := uint32(b ^ c) + x-- + return int32(x >> 31) +} + +// negative returns 1 if b < 0 and 0 otherwise. +func negative(b int32) int32 { + return (b >> 31) & 1 +} + +func PreComputedGroupElementCMove(t, u *PreComputedGroupElement, b int32) { + FeCMove(&t.yPlusX, &u.yPlusX, b) + FeCMove(&t.yMinusX, &u.yMinusX, b) + FeCMove(&t.xy2d, &u.xy2d, b) +} + +func selectPoint(t *PreComputedGroupElement, pos int32, b int32) { + var minusT PreComputedGroupElement + bNegative := negative(b) + bAbs := b - (((-bNegative) & b) << 1) + + t.Zero() + for i := int32(0); i < 8; i++ { + PreComputedGroupElementCMove(t, &base[pos][i], equal(bAbs, i+1)) + } + FeCopy(&minusT.yPlusX, &t.yMinusX) + FeCopy(&minusT.yMinusX, &t.yPlusX) + FeNeg(&minusT.xy2d, &t.xy2d) + PreComputedGroupElementCMove(t, &minusT, bNegative) +} + +// GeScalarMultBase computes h = a*B, where +// a = a[0]+256*a[1]+...+256^31 a[31] +// B is the Ed25519 base point (x,4/5) with x positive. +// +// Preconditions: +// a[31] <= 127 +func GeScalarMultBase(h *ExtendedGroupElement, a *[32]byte) { + var e [64]int8 + + for i, v := range a { + e[2*i] = int8(v & 15) + e[2*i+1] = int8((v >> 4) & 15) + } + + // each e[i] is between 0 and 15 and e[63] is between 0 and 7. + + carry := int8(0) + for i := 0; i < 63; i++ { + e[i] += carry + carry = (e[i] + 8) >> 4 + e[i] -= carry << 4 + } + e[63] += carry + // each e[i] is between -8 and 8. + + h.Zero() + var t PreComputedGroupElement + var r CompletedGroupElement + for i := int32(1); i < 64; i += 2 { + selectPoint(&t, i/2, int32(e[i])) + geMixedAdd(&r, h, &t) + r.ToExtended(h) + } + + var s ProjectiveGroupElement + + h.Double(&r) + r.ToProjective(&s) + s.Double(&r) + r.ToProjective(&s) + s.Double(&r) + r.ToProjective(&s) + s.Double(&r) + r.ToExtended(h) + + for i := int32(0); i < 64; i += 2 { + selectPoint(&t, i/2, int32(e[i])) + geMixedAdd(&r, h, &t) + r.ToExtended(h) + } +} + +// The scalars are GF(2^252 + 27742317777372353535851937790883648493). + +// Input: +// a[0]+256*a[1]+...+256^31*a[31] = a +// b[0]+256*b[1]+...+256^31*b[31] = b +// c[0]+256*c[1]+...+256^31*c[31] = c +// +// Output: +// s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l +// where l = 2^252 + 27742317777372353535851937790883648493. +func ScMulAdd(s, a, b, c *[32]byte) { + a0 := 2097151 & load3(a[:]) + a1 := 2097151 & (load4(a[2:]) >> 5) + a2 := 2097151 & (load3(a[5:]) >> 2) + a3 := 2097151 & (load4(a[7:]) >> 7) + a4 := 2097151 & (load4(a[10:]) >> 4) + a5 := 2097151 & (load3(a[13:]) >> 1) + a6 := 2097151 & (load4(a[15:]) >> 6) + a7 := 2097151 & (load3(a[18:]) >> 3) + a8 := 2097151 & load3(a[21:]) + a9 := 2097151 & (load4(a[23:]) >> 5) + a10 := 2097151 & (load3(a[26:]) >> 2) + a11 := (load4(a[28:]) >> 7) + b0 := 2097151 & load3(b[:]) + b1 := 2097151 & (load4(b[2:]) >> 5) + b2 := 2097151 & (load3(b[5:]) >> 2) + b3 := 2097151 & (load4(b[7:]) >> 7) + b4 := 2097151 & (load4(b[10:]) >> 4) + b5 := 2097151 & (load3(b[13:]) >> 1) + b6 := 2097151 & (load4(b[15:]) >> 6) + b7 := 2097151 & (load3(b[18:]) >> 3) + b8 := 2097151 & load3(b[21:]) + b9 := 2097151 & (load4(b[23:]) >> 5) + b10 := 2097151 & (load3(b[26:]) >> 2) + b11 := (load4(b[28:]) >> 7) + c0 := 2097151 & load3(c[:]) + c1 := 2097151 & (load4(c[2:]) >> 5) + c2 := 2097151 & (load3(c[5:]) >> 2) + c3 := 2097151 & (load4(c[7:]) >> 7) + c4 := 2097151 & (load4(c[10:]) >> 4) + c5 := 2097151 & (load3(c[13:]) >> 1) + c6 := 2097151 & (load4(c[15:]) >> 6) + c7 := 2097151 & (load3(c[18:]) >> 3) + c8 := 2097151 & load3(c[21:]) + c9 := 2097151 & (load4(c[23:]) >> 5) + c10 := 2097151 & (load3(c[26:]) >> 2) + c11 := (load4(c[28:]) >> 7) + var carry [23]int64 + + s0 := c0 + a0*b0 + s1 := c1 + a0*b1 + a1*b0 + s2 := c2 + a0*b2 + a1*b1 + a2*b0 + s3 := c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0 + s4 := c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0 + s5 := c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0 + s6 := c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0 + s7 := c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0 + s8 := c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0 + s9 := c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0 + s10 := c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0 + s11 := c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0 + s12 := a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1 + s13 := a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2 + s14 := a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3 + s15 := a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4 + s16 := a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5 + s17 := a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6 + s18 := a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7 + s19 := a8*b11 + a9*b10 + a10*b9 + a11*b8 + s20 := a9*b11 + a10*b10 + a11*b9 + s21 := a10*b11 + a11*b10 + s22 := a11 * b11 + s23 := int64(0) + + carry[0] = (s0 + (1 << 20)) >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[2] = (s2 + (1 << 20)) >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[4] = (s4 + (1 << 20)) >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[12] = (s12 + (1 << 20)) >> 21 + s13 += carry[12] + s12 -= carry[12] << 21 + carry[14] = (s14 + (1 << 20)) >> 21 + s15 += carry[14] + s14 -= carry[14] << 21 + carry[16] = (s16 + (1 << 20)) >> 21 + s17 += carry[16] + s16 -= carry[16] << 21 + carry[18] = (s18 + (1 << 20)) >> 21 + s19 += carry[18] + s18 -= carry[18] << 21 + carry[20] = (s20 + (1 << 20)) >> 21 + s21 += carry[20] + s20 -= carry[20] << 21 + carry[22] = (s22 + (1 << 20)) >> 21 + s23 += carry[22] + s22 -= carry[22] << 21 + + carry[1] = (s1 + (1 << 20)) >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[3] = (s3 + (1 << 20)) >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[5] = (s5 + (1 << 20)) >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + carry[13] = (s13 + (1 << 20)) >> 21 + s14 += carry[13] + s13 -= carry[13] << 21 + carry[15] = (s15 + (1 << 20)) >> 21 + s16 += carry[15] + s15 -= carry[15] << 21 + carry[17] = (s17 + (1 << 20)) >> 21 + s18 += carry[17] + s17 -= carry[17] << 21 + carry[19] = (s19 + (1 << 20)) >> 21 + s20 += carry[19] + s19 -= carry[19] << 21 + carry[21] = (s21 + (1 << 20)) >> 21 + s22 += carry[21] + s21 -= carry[21] << 21 + + s11 += s23 * 666643 + s12 += s23 * 470296 + s13 += s23 * 654183 + s14 -= s23 * 997805 + s15 += s23 * 136657 + s16 -= s23 * 683901 + s23 = 0 + + s10 += s22 * 666643 + s11 += s22 * 470296 + s12 += s22 * 654183 + s13 -= s22 * 997805 + s14 += s22 * 136657 + s15 -= s22 * 683901 + s22 = 0 + + s9 += s21 * 666643 + s10 += s21 * 470296 + s11 += s21 * 654183 + s12 -= s21 * 997805 + s13 += s21 * 136657 + s14 -= s21 * 683901 + s21 = 0 + + s8 += s20 * 666643 + s9 += s20 * 470296 + s10 += s20 * 654183 + s11 -= s20 * 997805 + s12 += s20 * 136657 + s13 -= s20 * 683901 + s20 = 0 + + s7 += s19 * 666643 + s8 += s19 * 470296 + s9 += s19 * 654183 + s10 -= s19 * 997805 + s11 += s19 * 136657 + s12 -= s19 * 683901 + s19 = 0 + + s6 += s18 * 666643 + s7 += s18 * 470296 + s8 += s18 * 654183 + s9 -= s18 * 997805 + s10 += s18 * 136657 + s11 -= s18 * 683901 + s18 = 0 + + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[12] = (s12 + (1 << 20)) >> 21 + s13 += carry[12] + s12 -= carry[12] << 21 + carry[14] = (s14 + (1 << 20)) >> 21 + s15 += carry[14] + s14 -= carry[14] << 21 + carry[16] = (s16 + (1 << 20)) >> 21 + s17 += carry[16] + s16 -= carry[16] << 21 + + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + carry[13] = (s13 + (1 << 20)) >> 21 + s14 += carry[13] + s13 -= carry[13] << 21 + carry[15] = (s15 + (1 << 20)) >> 21 + s16 += carry[15] + s15 -= carry[15] << 21 + + s5 += s17 * 666643 + s6 += s17 * 470296 + s7 += s17 * 654183 + s8 -= s17 * 997805 + s9 += s17 * 136657 + s10 -= s17 * 683901 + s17 = 0 + + s4 += s16 * 666643 + s5 += s16 * 470296 + s6 += s16 * 654183 + s7 -= s16 * 997805 + s8 += s16 * 136657 + s9 -= s16 * 683901 + s16 = 0 + + s3 += s15 * 666643 + s4 += s15 * 470296 + s5 += s15 * 654183 + s6 -= s15 * 997805 + s7 += s15 * 136657 + s8 -= s15 * 683901 + s15 = 0 + + s2 += s14 * 666643 + s3 += s14 * 470296 + s4 += s14 * 654183 + s5 -= s14 * 997805 + s6 += s14 * 136657 + s7 -= s14 * 683901 + s14 = 0 + + s1 += s13 * 666643 + s2 += s13 * 470296 + s3 += s13 * 654183 + s4 -= s13 * 997805 + s5 += s13 * 136657 + s6 -= s13 * 683901 + s13 = 0 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = (s0 + (1 << 20)) >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[2] = (s2 + (1 << 20)) >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[4] = (s4 + (1 << 20)) >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + + carry[1] = (s1 + (1 << 20)) >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[3] = (s3 + (1 << 20)) >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[5] = (s5 + (1 << 20)) >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = s0 >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[1] = s1 >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[2] = s2 >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[3] = s3 >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[4] = s4 >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[5] = s5 >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[6] = s6 >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[7] = s7 >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[8] = s8 >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[9] = s9 >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[10] = s10 >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[11] = s11 >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = s0 >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[1] = s1 >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[2] = s2 >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[3] = s3 >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[4] = s4 >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[5] = s5 >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[6] = s6 >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[7] = s7 >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[8] = s8 >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[9] = s9 >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[10] = s10 >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + + s[0] = byte(s0 >> 0) + s[1] = byte(s0 >> 8) + s[2] = byte((s0 >> 16) | (s1 << 5)) + s[3] = byte(s1 >> 3) + s[4] = byte(s1 >> 11) + s[5] = byte((s1 >> 19) | (s2 << 2)) + s[6] = byte(s2 >> 6) + s[7] = byte((s2 >> 14) | (s3 << 7)) + s[8] = byte(s3 >> 1) + s[9] = byte(s3 >> 9) + s[10] = byte((s3 >> 17) | (s4 << 4)) + s[11] = byte(s4 >> 4) + s[12] = byte(s4 >> 12) + s[13] = byte((s4 >> 20) | (s5 << 1)) + s[14] = byte(s5 >> 7) + s[15] = byte((s5 >> 15) | (s6 << 6)) + s[16] = byte(s6 >> 2) + s[17] = byte(s6 >> 10) + s[18] = byte((s6 >> 18) | (s7 << 3)) + s[19] = byte(s7 >> 5) + s[20] = byte(s7 >> 13) + s[21] = byte(s8 >> 0) + s[22] = byte(s8 >> 8) + s[23] = byte((s8 >> 16) | (s9 << 5)) + s[24] = byte(s9 >> 3) + s[25] = byte(s9 >> 11) + s[26] = byte((s9 >> 19) | (s10 << 2)) + s[27] = byte(s10 >> 6) + s[28] = byte((s10 >> 14) | (s11 << 7)) + s[29] = byte(s11 >> 1) + s[30] = byte(s11 >> 9) + s[31] = byte(s11 >> 17) +} + +// Input: +// s[0]+256*s[1]+...+256^63*s[63] = s +// +// Output: +// s[0]+256*s[1]+...+256^31*s[31] = s mod l +// where l = 2^252 + 27742317777372353535851937790883648493. +func ScReduce(out *[32]byte, s *[64]byte) { + s0 := 2097151 & load3(s[:]) + s1 := 2097151 & (load4(s[2:]) >> 5) + s2 := 2097151 & (load3(s[5:]) >> 2) + s3 := 2097151 & (load4(s[7:]) >> 7) + s4 := 2097151 & (load4(s[10:]) >> 4) + s5 := 2097151 & (load3(s[13:]) >> 1) + s6 := 2097151 & (load4(s[15:]) >> 6) + s7 := 2097151 & (load3(s[18:]) >> 3) + s8 := 2097151 & load3(s[21:]) + s9 := 2097151 & (load4(s[23:]) >> 5) + s10 := 2097151 & (load3(s[26:]) >> 2) + s11 := 2097151 & (load4(s[28:]) >> 7) + s12 := 2097151 & (load4(s[31:]) >> 4) + s13 := 2097151 & (load3(s[34:]) >> 1) + s14 := 2097151 & (load4(s[36:]) >> 6) + s15 := 2097151 & (load3(s[39:]) >> 3) + s16 := 2097151 & load3(s[42:]) + s17 := 2097151 & (load4(s[44:]) >> 5) + s18 := 2097151 & (load3(s[47:]) >> 2) + s19 := 2097151 & (load4(s[49:]) >> 7) + s20 := 2097151 & (load4(s[52:]) >> 4) + s21 := 2097151 & (load3(s[55:]) >> 1) + s22 := 2097151 & (load4(s[57:]) >> 6) + s23 := (load4(s[60:]) >> 3) + + s11 += s23 * 666643 + s12 += s23 * 470296 + s13 += s23 * 654183 + s14 -= s23 * 997805 + s15 += s23 * 136657 + s16 -= s23 * 683901 + s23 = 0 + + s10 += s22 * 666643 + s11 += s22 * 470296 + s12 += s22 * 654183 + s13 -= s22 * 997805 + s14 += s22 * 136657 + s15 -= s22 * 683901 + s22 = 0 + + s9 += s21 * 666643 + s10 += s21 * 470296 + s11 += s21 * 654183 + s12 -= s21 * 997805 + s13 += s21 * 136657 + s14 -= s21 * 683901 + s21 = 0 + + s8 += s20 * 666643 + s9 += s20 * 470296 + s10 += s20 * 654183 + s11 -= s20 * 997805 + s12 += s20 * 136657 + s13 -= s20 * 683901 + s20 = 0 + + s7 += s19 * 666643 + s8 += s19 * 470296 + s9 += s19 * 654183 + s10 -= s19 * 997805 + s11 += s19 * 136657 + s12 -= s19 * 683901 + s19 = 0 + + s6 += s18 * 666643 + s7 += s18 * 470296 + s8 += s18 * 654183 + s9 -= s18 * 997805 + s10 += s18 * 136657 + s11 -= s18 * 683901 + s18 = 0 + + var carry [17]int64 + + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[12] = (s12 + (1 << 20)) >> 21 + s13 += carry[12] + s12 -= carry[12] << 21 + carry[14] = (s14 + (1 << 20)) >> 21 + s15 += carry[14] + s14 -= carry[14] << 21 + carry[16] = (s16 + (1 << 20)) >> 21 + s17 += carry[16] + s16 -= carry[16] << 21 + + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + carry[13] = (s13 + (1 << 20)) >> 21 + s14 += carry[13] + s13 -= carry[13] << 21 + carry[15] = (s15 + (1 << 20)) >> 21 + s16 += carry[15] + s15 -= carry[15] << 21 + + s5 += s17 * 666643 + s6 += s17 * 470296 + s7 += s17 * 654183 + s8 -= s17 * 997805 + s9 += s17 * 136657 + s10 -= s17 * 683901 + s17 = 0 + + s4 += s16 * 666643 + s5 += s16 * 470296 + s6 += s16 * 654183 + s7 -= s16 * 997805 + s8 += s16 * 136657 + s9 -= s16 * 683901 + s16 = 0 + + s3 += s15 * 666643 + s4 += s15 * 470296 + s5 += s15 * 654183 + s6 -= s15 * 997805 + s7 += s15 * 136657 + s8 -= s15 * 683901 + s15 = 0 + + s2 += s14 * 666643 + s3 += s14 * 470296 + s4 += s14 * 654183 + s5 -= s14 * 997805 + s6 += s14 * 136657 + s7 -= s14 * 683901 + s14 = 0 + + s1 += s13 * 666643 + s2 += s13 * 470296 + s3 += s13 * 654183 + s4 -= s13 * 997805 + s5 += s13 * 136657 + s6 -= s13 * 683901 + s13 = 0 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = (s0 + (1 << 20)) >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[2] = (s2 + (1 << 20)) >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[4] = (s4 + (1 << 20)) >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + + carry[1] = (s1 + (1 << 20)) >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[3] = (s3 + (1 << 20)) >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[5] = (s5 + (1 << 20)) >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = s0 >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[1] = s1 >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[2] = s2 >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[3] = s3 >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[4] = s4 >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[5] = s5 >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[6] = s6 >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[7] = s7 >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[8] = s8 >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[9] = s9 >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[10] = s10 >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[11] = s11 >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = s0 >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[1] = s1 >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[2] = s2 >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[3] = s3 >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[4] = s4 >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[5] = s5 >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[6] = s6 >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[7] = s7 >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[8] = s8 >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[9] = s9 >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[10] = s10 >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + + out[0] = byte(s0 >> 0) + out[1] = byte(s0 >> 8) + out[2] = byte((s0 >> 16) | (s1 << 5)) + out[3] = byte(s1 >> 3) + out[4] = byte(s1 >> 11) + out[5] = byte((s1 >> 19) | (s2 << 2)) + out[6] = byte(s2 >> 6) + out[7] = byte((s2 >> 14) | (s3 << 7)) + out[8] = byte(s3 >> 1) + out[9] = byte(s3 >> 9) + out[10] = byte((s3 >> 17) | (s4 << 4)) + out[11] = byte(s4 >> 4) + out[12] = byte(s4 >> 12) + out[13] = byte((s4 >> 20) | (s5 << 1)) + out[14] = byte(s5 >> 7) + out[15] = byte((s5 >> 15) | (s6 << 6)) + out[16] = byte(s6 >> 2) + out[17] = byte(s6 >> 10) + out[18] = byte((s6 >> 18) | (s7 << 3)) + out[19] = byte(s7 >> 5) + out[20] = byte(s7 >> 13) + out[21] = byte(s8 >> 0) + out[22] = byte(s8 >> 8) + out[23] = byte((s8 >> 16) | (s9 << 5)) + out[24] = byte(s9 >> 3) + out[25] = byte(s9 >> 11) + out[26] = byte((s9 >> 19) | (s10 << 2)) + out[27] = byte(s10 >> 6) + out[28] = byte((s10 >> 14) | (s11 << 7)) + out[29] = byte(s11 >> 1) + out[30] = byte(s11 >> 9) + out[31] = byte(s11 >> 17) +} diff --git a/vendor/golang.org/x/crypto/ssh/buffer.go b/vendor/golang.org/x/crypto/ssh/buffer.go new file mode 100644 index 000000000..6931b5114 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/buffer.go @@ -0,0 +1,98 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "io" + "sync" +) + +// buffer provides a linked list buffer for data exchange +// between producer and consumer. Theoretically the buffer is +// of unlimited capacity as it does no allocation of its own. +type buffer struct { + // protects concurrent access to head, tail and closed + *sync.Cond + + head *element // the buffer that will be read first + tail *element // the buffer that will be read last + + closed bool +} + +// An element represents a single link in a linked list. +type element struct { + buf []byte + next *element +} + +// newBuffer returns an empty buffer that is not closed. +func newBuffer() *buffer { + e := new(element) + b := &buffer{ + Cond: newCond(), + head: e, + tail: e, + } + return b +} + +// write makes buf available for Read to receive. +// buf must not be modified after the call to write. +func (b *buffer) write(buf []byte) { + b.Cond.L.Lock() + e := &element{buf: buf} + b.tail.next = e + b.tail = e + b.Cond.Signal() + b.Cond.L.Unlock() +} + +// eof closes the buffer. Reads from the buffer once all +// the data has been consumed will receive os.EOF. +func (b *buffer) eof() error { + b.Cond.L.Lock() + b.closed = true + b.Cond.Signal() + b.Cond.L.Unlock() + return nil +} + +// Read reads data from the internal buffer in buf. Reads will block +// if no data is available, or until the buffer is closed. +func (b *buffer) Read(buf []byte) (n int, err error) { + b.Cond.L.Lock() + defer b.Cond.L.Unlock() + + for len(buf) > 0 { + // if there is data in b.head, copy it + if len(b.head.buf) > 0 { + r := copy(buf, b.head.buf) + buf, b.head.buf = buf[r:], b.head.buf[r:] + n += r + continue + } + // if there is a next buffer, make it the head + if len(b.head.buf) == 0 && b.head != b.tail { + b.head = b.head.next + continue + } + + // if at least one byte has been copied, return + if n > 0 { + break + } + + // if nothing was read, and there is nothing outstanding + // check to see if the buffer is closed. + if b.closed { + err = io.EOF + break + } + // out of buffers, wait for producer + b.Cond.Wait() + } + return +} diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go new file mode 100644 index 000000000..6331c94d5 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -0,0 +1,503 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" + "sort" + "time" +) + +// These constants from [PROTOCOL.certkeys] represent the algorithm names +// for certificate types supported by this package. +const ( + CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" + CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" + CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" + CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" + CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" + CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com" +) + +// Certificate types distinguish between host and user +// certificates. The values can be set in the CertType field of +// Certificate. +const ( + UserCert = 1 + HostCert = 2 +) + +// Signature represents a cryptographic signature. +type Signature struct { + Format string + Blob []byte +} + +// CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that +// a certificate does not expire. +const CertTimeInfinity = 1<<64 - 1 + +// An Certificate represents an OpenSSH certificate as defined in +// [PROTOCOL.certkeys]?rev=1.8. +type Certificate struct { + Nonce []byte + Key PublicKey + Serial uint64 + CertType uint32 + KeyId string + ValidPrincipals []string + ValidAfter uint64 + ValidBefore uint64 + Permissions + Reserved []byte + SignatureKey PublicKey + Signature *Signature +} + +// genericCertData holds the key-independent part of the certificate data. +// Overall, certificates contain an nonce, public key fields and +// key-independent fields. +type genericCertData struct { + Serial uint64 + CertType uint32 + KeyId string + ValidPrincipals []byte + ValidAfter uint64 + ValidBefore uint64 + CriticalOptions []byte + Extensions []byte + Reserved []byte + SignatureKey []byte + Signature []byte +} + +func marshalStringList(namelist []string) []byte { + var to []byte + for _, name := range namelist { + s := struct{ N string }{name} + to = append(to, Marshal(&s)...) + } + return to +} + +type optionsTuple struct { + Key string + Value []byte +} + +type optionsTupleValue struct { + Value string +} + +// serialize a map of critical options or extensions +// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, +// we need two length prefixes for a non-empty string value +func marshalTuples(tups map[string]string) []byte { + keys := make([]string, 0, len(tups)) + for key := range tups { + keys = append(keys, key) + } + sort.Strings(keys) + + var ret []byte + for _, key := range keys { + s := optionsTuple{Key: key} + if value := tups[key]; len(value) > 0 { + s.Value = Marshal(&optionsTupleValue{value}) + } + ret = append(ret, Marshal(&s)...) + } + return ret +} + +// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, +// we need two length prefixes for a non-empty option value +func parseTuples(in []byte) (map[string]string, error) { + tups := map[string]string{} + var lastKey string + var haveLastKey bool + + for len(in) > 0 { + var key, val, extra []byte + var ok bool + + if key, in, ok = parseString(in); !ok { + return nil, errShortRead + } + keyStr := string(key) + // according to [PROTOCOL.certkeys], the names must be in + // lexical order. + if haveLastKey && keyStr <= lastKey { + return nil, fmt.Errorf("ssh: certificate options are not in lexical order") + } + lastKey, haveLastKey = keyStr, true + // the next field is a data field, which if non-empty has a string embedded + if val, in, ok = parseString(in); !ok { + return nil, errShortRead + } + if len(val) > 0 { + val, extra, ok = parseString(val) + if !ok { + return nil, errShortRead + } + if len(extra) > 0 { + return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value") + } + tups[keyStr] = string(val) + } else { + tups[keyStr] = "" + } + } + return tups, nil +} + +func parseCert(in []byte, privAlgo string) (*Certificate, error) { + nonce, rest, ok := parseString(in) + if !ok { + return nil, errShortRead + } + + key, rest, err := parsePubKey(rest, privAlgo) + if err != nil { + return nil, err + } + + var g genericCertData + if err := Unmarshal(rest, &g); err != nil { + return nil, err + } + + c := &Certificate{ + Nonce: nonce, + Key: key, + Serial: g.Serial, + CertType: g.CertType, + KeyId: g.KeyId, + ValidAfter: g.ValidAfter, + ValidBefore: g.ValidBefore, + } + + for principals := g.ValidPrincipals; len(principals) > 0; { + principal, rest, ok := parseString(principals) + if !ok { + return nil, errShortRead + } + c.ValidPrincipals = append(c.ValidPrincipals, string(principal)) + principals = rest + } + + c.CriticalOptions, err = parseTuples(g.CriticalOptions) + if err != nil { + return nil, err + } + c.Extensions, err = parseTuples(g.Extensions) + if err != nil { + return nil, err + } + c.Reserved = g.Reserved + k, err := ParsePublicKey(g.SignatureKey) + if err != nil { + return nil, err + } + + c.SignatureKey = k + c.Signature, rest, ok = parseSignatureBody(g.Signature) + if !ok || len(rest) > 0 { + return nil, errors.New("ssh: signature parse error") + } + + return c, nil +} + +type openSSHCertSigner struct { + pub *Certificate + signer Signer +} + +// NewCertSigner returns a Signer that signs with the given Certificate, whose +// private key is held by signer. It returns an error if the public key in cert +// doesn't match the key used by signer. +func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { + if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { + return nil, errors.New("ssh: signer and cert have different public key") + } + + return &openSSHCertSigner{cert, signer}, nil +} + +func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { + return s.signer.Sign(rand, data) +} + +func (s *openSSHCertSigner) PublicKey() PublicKey { + return s.pub +} + +const sourceAddressCriticalOption = "source-address" + +// CertChecker does the work of verifying a certificate. Its methods +// can be plugged into ClientConfig.HostKeyCallback and +// ServerConfig.PublicKeyCallback. For the CertChecker to work, +// minimally, the IsAuthority callback should be set. +type CertChecker struct { + // SupportedCriticalOptions lists the CriticalOptions that the + // server application layer understands. These are only used + // for user certificates. + SupportedCriticalOptions []string + + // IsAuthority should return true if the key is recognized as + // an authority. This allows for certificates to be signed by other + // certificates. + IsAuthority func(auth PublicKey) bool + + // Clock is used for verifying time stamps. If nil, time.Now + // is used. + Clock func() time.Time + + // UserKeyFallback is called when CertChecker.Authenticate encounters a + // public key that is not a certificate. It must implement validation + // of user keys or else, if nil, all such keys are rejected. + UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) + + // HostKeyFallback is called when CertChecker.CheckHostKey encounters a + // public key that is not a certificate. It must implement host key + // validation or else, if nil, all such keys are rejected. + HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error + + // IsRevoked is called for each certificate so that revocation checking + // can be implemented. It should return true if the given certificate + // is revoked and false otherwise. If nil, no certificates are + // considered to have been revoked. + IsRevoked func(cert *Certificate) bool +} + +// CheckHostKey checks a host key certificate. This method can be +// plugged into ClientConfig.HostKeyCallback. +func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error { + cert, ok := key.(*Certificate) + if !ok { + if c.HostKeyFallback != nil { + return c.HostKeyFallback(addr, remote, key) + } + return errors.New("ssh: non-certificate host key") + } + if cert.CertType != HostCert { + return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) + } + + return c.CheckCert(addr, cert) +} + +// Authenticate checks a user certificate. Authenticate can be used as +// a value for ServerConfig.PublicKeyCallback. +func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) { + cert, ok := pubKey.(*Certificate) + if !ok { + if c.UserKeyFallback != nil { + return c.UserKeyFallback(conn, pubKey) + } + return nil, errors.New("ssh: normal key pairs not accepted") + } + + if cert.CertType != UserCert { + return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) + } + + if err := c.CheckCert(conn.User(), cert); err != nil { + return nil, err + } + + return &cert.Permissions, nil +} + +// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and +// the signature of the certificate. +func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { + if c.IsRevoked != nil && c.IsRevoked(cert) { + return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial) + } + + for opt, _ := range cert.CriticalOptions { + // sourceAddressCriticalOption will be enforced by + // serverAuthenticate + if opt == sourceAddressCriticalOption { + continue + } + + found := false + for _, supp := range c.SupportedCriticalOptions { + if supp == opt { + found = true + break + } + } + if !found { + return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt) + } + } + + if len(cert.ValidPrincipals) > 0 { + // By default, certs are valid for all users/hosts. + found := false + for _, p := range cert.ValidPrincipals { + if p == principal { + found = true + break + } + } + if !found { + return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals) + } + } + + if !c.IsAuthority(cert.SignatureKey) { + return fmt.Errorf("ssh: certificate signed by unrecognized authority") + } + + clock := c.Clock + if clock == nil { + clock = time.Now + } + + unixNow := clock().Unix() + if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { + return fmt.Errorf("ssh: cert is not yet valid") + } + if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { + return fmt.Errorf("ssh: cert has expired") + } + if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { + return fmt.Errorf("ssh: certificate signature does not verify") + } + + return nil +} + +// SignCert sets c.SignatureKey to the authority's public key and stores a +// Signature, by authority, in the certificate. +func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { + c.Nonce = make([]byte, 32) + if _, err := io.ReadFull(rand, c.Nonce); err != nil { + return err + } + c.SignatureKey = authority.PublicKey() + + sig, err := authority.Sign(rand, c.bytesForSigning()) + if err != nil { + return err + } + c.Signature = sig + return nil +} + +var certAlgoNames = map[string]string{ + KeyAlgoRSA: CertAlgoRSAv01, + KeyAlgoDSA: CertAlgoDSAv01, + KeyAlgoECDSA256: CertAlgoECDSA256v01, + KeyAlgoECDSA384: CertAlgoECDSA384v01, + KeyAlgoECDSA521: CertAlgoECDSA521v01, + KeyAlgoED25519: CertAlgoED25519v01, +} + +// certToPrivAlgo returns the underlying algorithm for a certificate algorithm. +// Panics if a non-certificate algorithm is passed. +func certToPrivAlgo(algo string) string { + for privAlgo, pubAlgo := range certAlgoNames { + if pubAlgo == algo { + return privAlgo + } + } + panic("unknown cert algorithm") +} + +func (cert *Certificate) bytesForSigning() []byte { + c2 := *cert + c2.Signature = nil + out := c2.Marshal() + // Drop trailing signature length. + return out[:len(out)-4] +} + +// Marshal serializes c into OpenSSH's wire format. It is part of the +// PublicKey interface. +func (c *Certificate) Marshal() []byte { + generic := genericCertData{ + Serial: c.Serial, + CertType: c.CertType, + KeyId: c.KeyId, + ValidPrincipals: marshalStringList(c.ValidPrincipals), + ValidAfter: uint64(c.ValidAfter), + ValidBefore: uint64(c.ValidBefore), + CriticalOptions: marshalTuples(c.CriticalOptions), + Extensions: marshalTuples(c.Extensions), + Reserved: c.Reserved, + SignatureKey: c.SignatureKey.Marshal(), + } + if c.Signature != nil { + generic.Signature = Marshal(c.Signature) + } + genericBytes := Marshal(&generic) + keyBytes := c.Key.Marshal() + _, keyBytes, _ = parseString(keyBytes) + prefix := Marshal(&struct { + Name string + Nonce []byte + Key []byte `ssh:"rest"` + }{c.Type(), c.Nonce, keyBytes}) + + result := make([]byte, 0, len(prefix)+len(genericBytes)) + result = append(result, prefix...) + result = append(result, genericBytes...) + return result +} + +// Type returns the key name. It is part of the PublicKey interface. +func (c *Certificate) Type() string { + algo, ok := certAlgoNames[c.Key.Type()] + if !ok { + panic("unknown cert key type " + c.Key.Type()) + } + return algo +} + +// Verify verifies a signature against the certificate's public +// key. It is part of the PublicKey interface. +func (c *Certificate) Verify(data []byte, sig *Signature) error { + return c.Key.Verify(data, sig) +} + +func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) { + format, in, ok := parseString(in) + if !ok { + return + } + + out = &Signature{ + Format: string(format), + } + + if out.Blob, in, ok = parseString(in); !ok { + return + } + + return out, in, ok +} + +func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) { + sigBytes, rest, ok := parseString(in) + if !ok { + return + } + + out, trailing, ok := parseSignatureBody(sigBytes) + if !ok || len(trailing) > 0 { + return nil, nil, false + } + return +} diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go new file mode 100644 index 000000000..195530ea0 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/channel.go @@ -0,0 +1,633 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "log" + "sync" +) + +const ( + minPacketLength = 9 + // channelMaxPacket contains the maximum number of bytes that will be + // sent in a single packet. As per RFC 4253, section 6.1, 32k is also + // the minimum. + channelMaxPacket = 1 << 15 + // We follow OpenSSH here. + channelWindowSize = 64 * channelMaxPacket +) + +// NewChannel represents an incoming request to a channel. It must either be +// accepted for use by calling Accept, or rejected by calling Reject. +type NewChannel interface { + // Accept accepts the channel creation request. It returns the Channel + // and a Go channel containing SSH requests. The Go channel must be + // serviced otherwise the Channel will hang. + Accept() (Channel, <-chan *Request, error) + + // Reject rejects the channel creation request. After calling + // this, no other methods on the Channel may be called. + Reject(reason RejectionReason, message string) error + + // ChannelType returns the type of the channel, as supplied by the + // client. + ChannelType() string + + // ExtraData returns the arbitrary payload for this channel, as supplied + // by the client. This data is specific to the channel type. + ExtraData() []byte +} + +// A Channel is an ordered, reliable, flow-controlled, duplex stream +// that is multiplexed over an SSH connection. +type Channel interface { + // Read reads up to len(data) bytes from the channel. + Read(data []byte) (int, error) + + // Write writes len(data) bytes to the channel. + Write(data []byte) (int, error) + + // Close signals end of channel use. No data may be sent after this + // call. + Close() error + + // CloseWrite signals the end of sending in-band + // data. Requests may still be sent, and the other side may + // still send data + CloseWrite() error + + // SendRequest sends a channel request. If wantReply is true, + // it will wait for a reply and return the result as a + // boolean, otherwise the return value will be false. Channel + // requests are out-of-band messages so they may be sent even + // if the data stream is closed or blocked by flow control. + // If the channel is closed before a reply is returned, io.EOF + // is returned. + SendRequest(name string, wantReply bool, payload []byte) (bool, error) + + // Stderr returns an io.ReadWriter that writes to this channel + // with the extended data type set to stderr. Stderr may + // safely be read and written from a different goroutine than + // Read and Write respectively. + Stderr() io.ReadWriter +} + +// Request is a request sent outside of the normal stream of +// data. Requests can either be specific to an SSH channel, or they +// can be global. +type Request struct { + Type string + WantReply bool + Payload []byte + + ch *channel + mux *mux +} + +// Reply sends a response to a request. It must be called for all requests +// where WantReply is true and is a no-op otherwise. The payload argument is +// ignored for replies to channel-specific requests. +func (r *Request) Reply(ok bool, payload []byte) error { + if !r.WantReply { + return nil + } + + if r.ch == nil { + return r.mux.ackRequest(ok, payload) + } + + return r.ch.ackRequest(ok) +} + +// RejectionReason is an enumeration used when rejecting channel creation +// requests. See RFC 4254, section 5.1. +type RejectionReason uint32 + +const ( + Prohibited RejectionReason = iota + 1 + ConnectionFailed + UnknownChannelType + ResourceShortage +) + +// String converts the rejection reason to human readable form. +func (r RejectionReason) String() string { + switch r { + case Prohibited: + return "administratively prohibited" + case ConnectionFailed: + return "connect failed" + case UnknownChannelType: + return "unknown channel type" + case ResourceShortage: + return "resource shortage" + } + return fmt.Sprintf("unknown reason %d", int(r)) +} + +func min(a uint32, b int) uint32 { + if a < uint32(b) { + return a + } + return uint32(b) +} + +type channelDirection uint8 + +const ( + channelInbound channelDirection = iota + channelOutbound +) + +// channel is an implementation of the Channel interface that works +// with the mux class. +type channel struct { + // R/O after creation + chanType string + extraData []byte + localId, remoteId uint32 + + // maxIncomingPayload and maxRemotePayload are the maximum + // payload sizes of normal and extended data packets for + // receiving and sending, respectively. The wire packet will + // be 9 or 13 bytes larger (excluding encryption overhead). + maxIncomingPayload uint32 + maxRemotePayload uint32 + + mux *mux + + // decided is set to true if an accept or reject message has been sent + // (for outbound channels) or received (for inbound channels). + decided bool + + // direction contains either channelOutbound, for channels created + // locally, or channelInbound, for channels created by the peer. + direction channelDirection + + // Pending internal channel messages. + msg chan interface{} + + // Since requests have no ID, there can be only one request + // with WantReply=true outstanding. This lock is held by a + // goroutine that has such an outgoing request pending. + sentRequestMu sync.Mutex + + incomingRequests chan *Request + + sentEOF bool + + // thread-safe data + remoteWin window + pending *buffer + extPending *buffer + + // windowMu protects myWindow, the flow-control window. + windowMu sync.Mutex + myWindow uint32 + + // writeMu serializes calls to mux.conn.writePacket() and + // protects sentClose and packetPool. This mutex must be + // different from windowMu, as writePacket can block if there + // is a key exchange pending. + writeMu sync.Mutex + sentClose bool + + // packetPool has a buffer for each extended channel ID to + // save allocations during writes. + packetPool map[uint32][]byte +} + +// writePacket sends a packet. If the packet is a channel close, it updates +// sentClose. This method takes the lock c.writeMu. +func (c *channel) writePacket(packet []byte) error { + c.writeMu.Lock() + if c.sentClose { + c.writeMu.Unlock() + return io.EOF + } + c.sentClose = (packet[0] == msgChannelClose) + err := c.mux.conn.writePacket(packet) + c.writeMu.Unlock() + return err +} + +func (c *channel) sendMessage(msg interface{}) error { + if debugMux { + log.Printf("send(%d): %#v", c.mux.chanList.offset, msg) + } + + p := Marshal(msg) + binary.BigEndian.PutUint32(p[1:], c.remoteId) + return c.writePacket(p) +} + +// WriteExtended writes data to a specific extended stream. These streams are +// used, for example, for stderr. +func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) { + if c.sentEOF { + return 0, io.EOF + } + // 1 byte message type, 4 bytes remoteId, 4 bytes data length + opCode := byte(msgChannelData) + headerLength := uint32(9) + if extendedCode > 0 { + headerLength += 4 + opCode = msgChannelExtendedData + } + + c.writeMu.Lock() + packet := c.packetPool[extendedCode] + // We don't remove the buffer from packetPool, so + // WriteExtended calls from different goroutines will be + // flagged as errors by the race detector. + c.writeMu.Unlock() + + for len(data) > 0 { + space := min(c.maxRemotePayload, len(data)) + if space, err = c.remoteWin.reserve(space); err != nil { + return n, err + } + if want := headerLength + space; uint32(cap(packet)) < want { + packet = make([]byte, want) + } else { + packet = packet[:want] + } + + todo := data[:space] + + packet[0] = opCode + binary.BigEndian.PutUint32(packet[1:], c.remoteId) + if extendedCode > 0 { + binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode)) + } + binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo))) + copy(packet[headerLength:], todo) + if err = c.writePacket(packet); err != nil { + return n, err + } + + n += len(todo) + data = data[len(todo):] + } + + c.writeMu.Lock() + c.packetPool[extendedCode] = packet + c.writeMu.Unlock() + + return n, err +} + +func (c *channel) handleData(packet []byte) error { + headerLen := 9 + isExtendedData := packet[0] == msgChannelExtendedData + if isExtendedData { + headerLen = 13 + } + if len(packet) < headerLen { + // malformed data packet + return parseError(packet[0]) + } + + var extended uint32 + if isExtendedData { + extended = binary.BigEndian.Uint32(packet[5:]) + } + + length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen]) + if length == 0 { + return nil + } + if length > c.maxIncomingPayload { + // TODO(hanwen): should send Disconnect? + return errors.New("ssh: incoming packet exceeds maximum payload size") + } + + data := packet[headerLen:] + if length != uint32(len(data)) { + return errors.New("ssh: wrong packet length") + } + + c.windowMu.Lock() + if c.myWindow < length { + c.windowMu.Unlock() + // TODO(hanwen): should send Disconnect with reason? + return errors.New("ssh: remote side wrote too much") + } + c.myWindow -= length + c.windowMu.Unlock() + + if extended == 1 { + c.extPending.write(data) + } else if extended > 0 { + // discard other extended data. + } else { + c.pending.write(data) + } + return nil +} + +func (c *channel) adjustWindow(n uint32) error { + c.windowMu.Lock() + // Since myWindow is managed on our side, and can never exceed + // the initial window setting, we don't worry about overflow. + c.myWindow += uint32(n) + c.windowMu.Unlock() + return c.sendMessage(windowAdjustMsg{ + AdditionalBytes: uint32(n), + }) +} + +func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) { + switch extended { + case 1: + n, err = c.extPending.Read(data) + case 0: + n, err = c.pending.Read(data) + default: + return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended) + } + + if n > 0 { + err = c.adjustWindow(uint32(n)) + // sendWindowAdjust can return io.EOF if the remote + // peer has closed the connection, however we want to + // defer forwarding io.EOF to the caller of Read until + // the buffer has been drained. + if n > 0 && err == io.EOF { + err = nil + } + } + + return n, err +} + +func (c *channel) close() { + c.pending.eof() + c.extPending.eof() + close(c.msg) + close(c.incomingRequests) + c.writeMu.Lock() + // This is not necessary for a normal channel teardown, but if + // there was another error, it is. + c.sentClose = true + c.writeMu.Unlock() + // Unblock writers. + c.remoteWin.close() +} + +// responseMessageReceived is called when a success or failure message is +// received on a channel to check that such a message is reasonable for the +// given channel. +func (c *channel) responseMessageReceived() error { + if c.direction == channelInbound { + return errors.New("ssh: channel response message received on inbound channel") + } + if c.decided { + return errors.New("ssh: duplicate response received for channel") + } + c.decided = true + return nil +} + +func (c *channel) handlePacket(packet []byte) error { + switch packet[0] { + case msgChannelData, msgChannelExtendedData: + return c.handleData(packet) + case msgChannelClose: + c.sendMessage(channelCloseMsg{PeersId: c.remoteId}) + c.mux.chanList.remove(c.localId) + c.close() + return nil + case msgChannelEOF: + // RFC 4254 is mute on how EOF affects dataExt messages but + // it is logical to signal EOF at the same time. + c.extPending.eof() + c.pending.eof() + return nil + } + + decoded, err := decode(packet) + if err != nil { + return err + } + + switch msg := decoded.(type) { + case *channelOpenFailureMsg: + if err := c.responseMessageReceived(); err != nil { + return err + } + c.mux.chanList.remove(msg.PeersId) + c.msg <- msg + case *channelOpenConfirmMsg: + if err := c.responseMessageReceived(); err != nil { + return err + } + if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { + return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize) + } + c.remoteId = msg.MyId + c.maxRemotePayload = msg.MaxPacketSize + c.remoteWin.add(msg.MyWindow) + c.msg <- msg + case *windowAdjustMsg: + if !c.remoteWin.add(msg.AdditionalBytes) { + return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes) + } + case *channelRequestMsg: + req := Request{ + Type: msg.Request, + WantReply: msg.WantReply, + Payload: msg.RequestSpecificData, + ch: c, + } + + c.incomingRequests <- &req + default: + c.msg <- msg + } + return nil +} + +func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel { + ch := &channel{ + remoteWin: window{Cond: newCond()}, + myWindow: channelWindowSize, + pending: newBuffer(), + extPending: newBuffer(), + direction: direction, + incomingRequests: make(chan *Request, chanSize), + msg: make(chan interface{}, chanSize), + chanType: chanType, + extraData: extraData, + mux: m, + packetPool: make(map[uint32][]byte), + } + ch.localId = m.chanList.add(ch) + return ch +} + +var errUndecided = errors.New("ssh: must Accept or Reject channel") +var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once") + +type extChannel struct { + code uint32 + ch *channel +} + +func (e *extChannel) Write(data []byte) (n int, err error) { + return e.ch.WriteExtended(data, e.code) +} + +func (e *extChannel) Read(data []byte) (n int, err error) { + return e.ch.ReadExtended(data, e.code) +} + +func (c *channel) Accept() (Channel, <-chan *Request, error) { + if c.decided { + return nil, nil, errDecidedAlready + } + c.maxIncomingPayload = channelMaxPacket + confirm := channelOpenConfirmMsg{ + PeersId: c.remoteId, + MyId: c.localId, + MyWindow: c.myWindow, + MaxPacketSize: c.maxIncomingPayload, + } + c.decided = true + if err := c.sendMessage(confirm); err != nil { + return nil, nil, err + } + + return c, c.incomingRequests, nil +} + +func (ch *channel) Reject(reason RejectionReason, message string) error { + if ch.decided { + return errDecidedAlready + } + reject := channelOpenFailureMsg{ + PeersId: ch.remoteId, + Reason: reason, + Message: message, + Language: "en", + } + ch.decided = true + return ch.sendMessage(reject) +} + +func (ch *channel) Read(data []byte) (int, error) { + if !ch.decided { + return 0, errUndecided + } + return ch.ReadExtended(data, 0) +} + +func (ch *channel) Write(data []byte) (int, error) { + if !ch.decided { + return 0, errUndecided + } + return ch.WriteExtended(data, 0) +} + +func (ch *channel) CloseWrite() error { + if !ch.decided { + return errUndecided + } + ch.sentEOF = true + return ch.sendMessage(channelEOFMsg{ + PeersId: ch.remoteId}) +} + +func (ch *channel) Close() error { + if !ch.decided { + return errUndecided + } + + return ch.sendMessage(channelCloseMsg{ + PeersId: ch.remoteId}) +} + +// Extended returns an io.ReadWriter that sends and receives data on the given, +// SSH extended stream. Such streams are used, for example, for stderr. +func (ch *channel) Extended(code uint32) io.ReadWriter { + if !ch.decided { + return nil + } + return &extChannel{code, ch} +} + +func (ch *channel) Stderr() io.ReadWriter { + return ch.Extended(1) +} + +func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { + if !ch.decided { + return false, errUndecided + } + + if wantReply { + ch.sentRequestMu.Lock() + defer ch.sentRequestMu.Unlock() + } + + msg := channelRequestMsg{ + PeersId: ch.remoteId, + Request: name, + WantReply: wantReply, + RequestSpecificData: payload, + } + + if err := ch.sendMessage(msg); err != nil { + return false, err + } + + if wantReply { + m, ok := (<-ch.msg) + if !ok { + return false, io.EOF + } + switch m.(type) { + case *channelRequestFailureMsg: + return false, nil + case *channelRequestSuccessMsg: + return true, nil + default: + return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m) + } + } + + return false, nil +} + +// ackRequest either sends an ack or nack to the channel request. +func (ch *channel) ackRequest(ok bool) error { + if !ch.decided { + return errUndecided + } + + var msg interface{} + if !ok { + msg = channelRequestFailureMsg{ + PeersId: ch.remoteId, + } + } else { + msg = channelRequestSuccessMsg{ + PeersId: ch.remoteId, + } + } + return ch.sendMessage(msg) +} + +func (ch *channel) ChannelType() string { + return ch.chanType +} + +func (ch *channel) ExtraData() []byte { + return ch.extraData +} diff --git a/vendor/golang.org/x/crypto/ssh/cipher.go b/vendor/golang.org/x/crypto/ssh/cipher.go new file mode 100644 index 000000000..13484ab4b --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/cipher.go @@ -0,0 +1,627 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rc4" + "crypto/subtle" + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + "io/ioutil" +) + +const ( + packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. + + // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations + // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC + // indicates implementations SHOULD be able to handle larger packet sizes, but then + // waffles on about reasonable limits. + // + // OpenSSH caps their maxPacket at 256kB so we choose to do + // the same. maxPacket is also used to ensure that uint32 + // length fields do not overflow, so it should remain well + // below 4G. + maxPacket = 256 * 1024 +) + +// noneCipher implements cipher.Stream and provides no encryption. It is used +// by the transport before the first key-exchange. +type noneCipher struct{} + +func (c noneCipher) XORKeyStream(dst, src []byte) { + copy(dst, src) +} + +func newAESCTR(key, iv []byte) (cipher.Stream, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewCTR(c, iv), nil +} + +func newRC4(key, iv []byte) (cipher.Stream, error) { + return rc4.NewCipher(key) +} + +type streamCipherMode struct { + keySize int + ivSize int + skip int + createFunc func(key, iv []byte) (cipher.Stream, error) +} + +func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) { + if len(key) < c.keySize { + panic("ssh: key length too small for cipher") + } + if len(iv) < c.ivSize { + panic("ssh: iv too small for cipher") + } + + stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize]) + if err != nil { + return nil, err + } + + var streamDump []byte + if c.skip > 0 { + streamDump = make([]byte, 512) + } + + for remainingToDump := c.skip; remainingToDump > 0; { + dumpThisTime := remainingToDump + if dumpThisTime > len(streamDump) { + dumpThisTime = len(streamDump) + } + stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) + remainingToDump -= dumpThisTime + } + + return stream, nil +} + +// cipherModes documents properties of supported ciphers. Ciphers not included +// are not supported and will not be negotiated, even if explicitly requested in +// ClientConfig.Crypto.Ciphers. +var cipherModes = map[string]*streamCipherMode{ + // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms + // are defined in the order specified in the RFC. + "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, + "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, + "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, + + // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. + // They are defined in the order specified in the RFC. + "arcfour128": {16, 0, 1536, newRC4}, + "arcfour256": {32, 0, 1536, newRC4}, + + // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. + // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and + // RC4) has problems with weak keys, and should be used with caution." + // RFC4345 introduces improved versions of Arcfour. + "arcfour": {16, 0, 0, newRC4}, + + // AES-GCM is not a stream cipher, so it is constructed with a + // special case. If we add any more non-stream ciphers, we + // should invest a cleaner way to do this. + gcmCipherID: {16, 12, 0, nil}, + + // CBC mode is insecure and so is not included in the default config. + // (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely + // needed, it's possible to specify a custom Config to enable it. + // You should expect that an active attacker can recover plaintext if + // you do. + aes128cbcID: {16, aes.BlockSize, 0, nil}, + + // 3des-cbc is insecure and is disabled by default. + tripledescbcID: {24, des.BlockSize, 0, nil}, +} + +// prefixLen is the length of the packet prefix that contains the packet length +// and number of padding bytes. +const prefixLen = 5 + +// streamPacketCipher is a packetCipher using a stream cipher. +type streamPacketCipher struct { + mac hash.Hash + cipher cipher.Stream + etm bool + + // The following members are to avoid per-packet allocations. + prefix [prefixLen]byte + seqNumBytes [4]byte + padding [2 * packetSizeMultiple]byte + packetData []byte + macResult []byte +} + +// readPacket reads and decrypt a single packet from the reader argument. +func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { + if _, err := io.ReadFull(r, s.prefix[:]); err != nil { + return nil, err + } + + var encryptedPaddingLength [1]byte + if s.mac != nil && s.etm { + copy(encryptedPaddingLength[:], s.prefix[4:5]) + s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) + } else { + s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) + } + + length := binary.BigEndian.Uint32(s.prefix[0:4]) + paddingLength := uint32(s.prefix[4]) + + var macSize uint32 + if s.mac != nil { + s.mac.Reset() + binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) + s.mac.Write(s.seqNumBytes[:]) + if s.etm { + s.mac.Write(s.prefix[:4]) + s.mac.Write(encryptedPaddingLength[:]) + } else { + s.mac.Write(s.prefix[:]) + } + macSize = uint32(s.mac.Size()) + } + + if length <= paddingLength+1 { + return nil, errors.New("ssh: invalid packet length, packet too small") + } + + if length > maxPacket { + return nil, errors.New("ssh: invalid packet length, packet too large") + } + + // the maxPacket check above ensures that length-1+macSize + // does not overflow. + if uint32(cap(s.packetData)) < length-1+macSize { + s.packetData = make([]byte, length-1+macSize) + } else { + s.packetData = s.packetData[:length-1+macSize] + } + + if _, err := io.ReadFull(r, s.packetData); err != nil { + return nil, err + } + mac := s.packetData[length-1:] + data := s.packetData[:length-1] + + if s.mac != nil && s.etm { + s.mac.Write(data) + } + + s.cipher.XORKeyStream(data, data) + + if s.mac != nil { + if !s.etm { + s.mac.Write(data) + } + s.macResult = s.mac.Sum(s.macResult[:0]) + if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { + return nil, errors.New("ssh: MAC failure") + } + } + + return s.packetData[:length-paddingLength-1], nil +} + +// writePacket encrypts and sends a packet of data to the writer argument +func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + if len(packet) > maxPacket { + return errors.New("ssh: packet too large") + } + + aadlen := 0 + if s.mac != nil && s.etm { + // packet length is not encrypted for EtM modes + aadlen = 4 + } + + paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple + if paddingLength < 4 { + paddingLength += packetSizeMultiple + } + + length := len(packet) + 1 + paddingLength + binary.BigEndian.PutUint32(s.prefix[:], uint32(length)) + s.prefix[4] = byte(paddingLength) + padding := s.padding[:paddingLength] + if _, err := io.ReadFull(rand, padding); err != nil { + return err + } + + if s.mac != nil { + s.mac.Reset() + binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) + s.mac.Write(s.seqNumBytes[:]) + + if s.etm { + // For EtM algorithms, the packet length must stay unencrypted, + // but the following data (padding length) must be encrypted + s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) + } + + s.mac.Write(s.prefix[:]) + + if !s.etm { + // For non-EtM algorithms, the algorithm is applied on unencrypted data + s.mac.Write(packet) + s.mac.Write(padding) + } + } + + if !(s.mac != nil && s.etm) { + // For EtM algorithms, the padding length has already been encrypted + // and the packet length must remain unencrypted + s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) + } + + s.cipher.XORKeyStream(packet, packet) + s.cipher.XORKeyStream(padding, padding) + + if s.mac != nil && s.etm { + // For EtM algorithms, packet and padding must be encrypted + s.mac.Write(packet) + s.mac.Write(padding) + } + + if _, err := w.Write(s.prefix[:]); err != nil { + return err + } + if _, err := w.Write(packet); err != nil { + return err + } + if _, err := w.Write(padding); err != nil { + return err + } + + if s.mac != nil { + s.macResult = s.mac.Sum(s.macResult[:0]) + if _, err := w.Write(s.macResult); err != nil { + return err + } + } + + return nil +} + +type gcmCipher struct { + aead cipher.AEAD + prefix [4]byte + iv []byte + buf []byte +} + +func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + aead, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + return &gcmCipher{ + aead: aead, + iv: iv, + }, nil +} + +const gcmTagSize = 16 + +func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + // Pad out to multiple of 16 bytes. This is different from the + // stream cipher because that encrypts the length too. + padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple) + if padding < 4 { + padding += packetSizeMultiple + } + + length := uint32(len(packet) + int(padding) + 1) + binary.BigEndian.PutUint32(c.prefix[:], length) + if _, err := w.Write(c.prefix[:]); err != nil { + return err + } + + if cap(c.buf) < int(length) { + c.buf = make([]byte, length) + } else { + c.buf = c.buf[:length] + } + + c.buf[0] = padding + copy(c.buf[1:], packet) + if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil { + return err + } + c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:]) + if _, err := w.Write(c.buf); err != nil { + return err + } + c.incIV() + + return nil +} + +func (c *gcmCipher) incIV() { + for i := 4 + 7; i >= 4; i-- { + c.iv[i]++ + if c.iv[i] != 0 { + break + } + } +} + +func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { + if _, err := io.ReadFull(r, c.prefix[:]); err != nil { + return nil, err + } + length := binary.BigEndian.Uint32(c.prefix[:]) + if length > maxPacket { + return nil, errors.New("ssh: max packet length exceeded.") + } + + if cap(c.buf) < int(length+gcmTagSize) { + c.buf = make([]byte, length+gcmTagSize) + } else { + c.buf = c.buf[:length+gcmTagSize] + } + + if _, err := io.ReadFull(r, c.buf); err != nil { + return nil, err + } + + plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:]) + if err != nil { + return nil, err + } + c.incIV() + + padding := plain[0] + if padding < 4 || padding >= 20 { + return nil, fmt.Errorf("ssh: illegal padding %d", padding) + } + + if int(padding+1) >= len(plain) { + return nil, fmt.Errorf("ssh: padding %d too large", padding) + } + plain = plain[1 : length-uint32(padding)] + return plain, nil +} + +// cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1 +type cbcCipher struct { + mac hash.Hash + macSize uint32 + decrypter cipher.BlockMode + encrypter cipher.BlockMode + + // The following members are to avoid per-packet allocations. + seqNumBytes [4]byte + packetData []byte + macResult []byte + + // Amount of data we should still read to hide which + // verification error triggered. + oracleCamouflage uint32 +} + +func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + cbc := &cbcCipher{ + mac: macModes[algs.MAC].new(macKey), + decrypter: cipher.NewCBCDecrypter(c, iv), + encrypter: cipher.NewCBCEncrypter(c, iv), + packetData: make([]byte, 1024), + } + if cbc.mac != nil { + cbc.macSize = uint32(cbc.mac.Size()) + } + + return cbc, nil +} + +func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + cbc, err := newCBCCipher(c, iv, key, macKey, algs) + if err != nil { + return nil, err + } + + return cbc, nil +} + +func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + c, err := des.NewTripleDESCipher(key) + if err != nil { + return nil, err + } + + cbc, err := newCBCCipher(c, iv, key, macKey, algs) + if err != nil { + return nil, err + } + + return cbc, nil +} + +func maxUInt32(a, b int) uint32 { + if a > b { + return uint32(a) + } + return uint32(b) +} + +const ( + cbcMinPacketSizeMultiple = 8 + cbcMinPacketSize = 16 + cbcMinPaddingSize = 4 +) + +// cbcError represents a verification error that may leak information. +type cbcError string + +func (e cbcError) Error() string { return string(e) } + +func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { + p, err := c.readPacketLeaky(seqNum, r) + if err != nil { + if _, ok := err.(cbcError); ok { + // Verification error: read a fixed amount of + // data, to make distinguishing between + // failing MAC and failing length check more + // difficult. + io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage)) + } + } + return p, err +} + +func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) { + blockSize := c.decrypter.BlockSize() + + // Read the header, which will include some of the subsequent data in the + // case of block ciphers - this is copied back to the payload later. + // How many bytes of payload/padding will be read with this first read. + firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize) + firstBlock := c.packetData[:firstBlockLength] + if _, err := io.ReadFull(r, firstBlock); err != nil { + return nil, err + } + + c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength + + c.decrypter.CryptBlocks(firstBlock, firstBlock) + length := binary.BigEndian.Uint32(firstBlock[:4]) + if length > maxPacket { + return nil, cbcError("ssh: packet too large") + } + if length+4 < maxUInt32(cbcMinPacketSize, blockSize) { + // The minimum size of a packet is 16 (or the cipher block size, whichever + // is larger) bytes. + return nil, cbcError("ssh: packet too small") + } + // The length of the packet (including the length field but not the MAC) must + // be a multiple of the block size or 8, whichever is larger. + if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 { + return nil, cbcError("ssh: invalid packet length multiple") + } + + paddingLength := uint32(firstBlock[4]) + if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 { + return nil, cbcError("ssh: invalid packet length") + } + + // Positions within the c.packetData buffer: + macStart := 4 + length + paddingStart := macStart - paddingLength + + // Entire packet size, starting before length, ending at end of mac. + entirePacketSize := macStart + c.macSize + + // Ensure c.packetData is large enough for the entire packet data. + if uint32(cap(c.packetData)) < entirePacketSize { + // Still need to upsize and copy, but this should be rare at runtime, only + // on upsizing the packetData buffer. + c.packetData = make([]byte, entirePacketSize) + copy(c.packetData, firstBlock) + } else { + c.packetData = c.packetData[:entirePacketSize] + } + + if n, err := io.ReadFull(r, c.packetData[firstBlockLength:]); err != nil { + return nil, err + } else { + c.oracleCamouflage -= uint32(n) + } + + remainingCrypted := c.packetData[firstBlockLength:macStart] + c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted) + + mac := c.packetData[macStart:] + if c.mac != nil { + c.mac.Reset() + binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) + c.mac.Write(c.seqNumBytes[:]) + c.mac.Write(c.packetData[:macStart]) + c.macResult = c.mac.Sum(c.macResult[:0]) + if subtle.ConstantTimeCompare(c.macResult, mac) != 1 { + return nil, cbcError("ssh: MAC failure") + } + } + + return c.packetData[prefixLen:paddingStart], nil +} + +func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize()) + + // Length of encrypted portion of the packet (header, payload, padding). + // Enforce minimum padding and packet size. + encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize) + // Enforce block size. + encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize + + length := encLength - 4 + paddingLength := int(length) - (1 + len(packet)) + + // Overall buffer contains: header, payload, padding, mac. + // Space for the MAC is reserved in the capacity but not the slice length. + bufferSize := encLength + c.macSize + if uint32(cap(c.packetData)) < bufferSize { + c.packetData = make([]byte, encLength, bufferSize) + } else { + c.packetData = c.packetData[:encLength] + } + + p := c.packetData + + // Packet header. + binary.BigEndian.PutUint32(p, length) + p = p[4:] + p[0] = byte(paddingLength) + + // Payload. + p = p[1:] + copy(p, packet) + + // Padding. + p = p[len(packet):] + if _, err := io.ReadFull(rand, p); err != nil { + return err + } + + if c.mac != nil { + c.mac.Reset() + binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) + c.mac.Write(c.seqNumBytes[:]) + c.mac.Write(c.packetData) + // The MAC is now appended into the capacity reserved for it earlier. + c.packetData = c.mac.Sum(c.packetData) + } + + c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength]) + + if _, err := w.Write(c.packetData); err != nil { + return err + } + + return nil +} diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go new file mode 100644 index 000000000..c97f2978e --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -0,0 +1,211 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "errors" + "fmt" + "net" + "sync" + "time" +) + +// Client implements a traditional SSH client that supports shells, +// subprocesses, port forwarding and tunneled dialing. +type Client struct { + Conn + + forwards forwardList // forwarded tcpip connections from the remote side + mu sync.Mutex + channelHandlers map[string]chan NewChannel +} + +// HandleChannelOpen returns a channel on which NewChannel requests +// for the given type are sent. If the type already is being handled, +// nil is returned. The channel is closed when the connection is closed. +func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel { + c.mu.Lock() + defer c.mu.Unlock() + if c.channelHandlers == nil { + // The SSH channel has been closed. + c := make(chan NewChannel) + close(c) + return c + } + + ch := c.channelHandlers[channelType] + if ch != nil { + return nil + } + + ch = make(chan NewChannel, chanSize) + c.channelHandlers[channelType] = ch + return ch +} + +// NewClient creates a Client on top of the given connection. +func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { + conn := &Client{ + Conn: c, + channelHandlers: make(map[string]chan NewChannel, 1), + } + + go conn.handleGlobalRequests(reqs) + go conn.handleChannelOpens(chans) + go func() { + conn.Wait() + conn.forwards.closeAll() + }() + go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip")) + return conn +} + +// NewClientConn establishes an authenticated SSH connection using c +// as the underlying transport. The Request and NewChannel channels +// must be serviced or the connection will hang. +func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { + fullConf := *config + fullConf.SetDefaults() + conn := &connection{ + sshConn: sshConn{conn: c}, + } + + if err := conn.clientHandshake(addr, &fullConf); err != nil { + c.Close() + return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) + } + conn.mux = newMux(conn.transport) + return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil +} + +// clientHandshake performs the client side key exchange. See RFC 4253 Section +// 7. +func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error { + if config.ClientVersion != "" { + c.clientVersion = []byte(config.ClientVersion) + } else { + c.clientVersion = []byte(packageVersion) + } + var err error + c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion) + if err != nil { + return err + } + + c.transport = newClientTransport( + newTransport(c.sshConn.conn, config.Rand, true /* is client */), + c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) + if err := c.transport.waitSession(); err != nil { + return err + } + + c.sessionID = c.transport.getSessionID() + return c.clientAuthenticate(config) +} + +// verifyHostKeySignature verifies the host key obtained in the key +// exchange. +func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error { + sig, rest, ok := parseSignatureBody(result.Signature) + if len(rest) > 0 || !ok { + return errors.New("ssh: signature parse error") + } + + return hostKey.Verify(result.H, sig) +} + +// NewSession opens a new Session for this client. (A session is a remote +// execution of a program.) +func (c *Client) NewSession() (*Session, error) { + ch, in, err := c.OpenChannel("session", nil) + if err != nil { + return nil, err + } + return newSession(ch, in) +} + +func (c *Client) handleGlobalRequests(incoming <-chan *Request) { + for r := range incoming { + // This handles keepalive messages and matches + // the behaviour of OpenSSH. + r.Reply(false, nil) + } +} + +// handleChannelOpens channel open messages from the remote side. +func (c *Client) handleChannelOpens(in <-chan NewChannel) { + for ch := range in { + c.mu.Lock() + handler := c.channelHandlers[ch.ChannelType()] + c.mu.Unlock() + + if handler != nil { + handler <- ch + } else { + ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType())) + } + } + + c.mu.Lock() + for _, ch := range c.channelHandlers { + close(ch) + } + c.channelHandlers = nil + c.mu.Unlock() +} + +// Dial starts a client connection to the given SSH server. It is a +// convenience function that connects to the given network address, +// initiates the SSH handshake, and then sets up a Client. For access +// to incoming channels and requests, use net.Dial with NewClientConn +// instead. +func Dial(network, addr string, config *ClientConfig) (*Client, error) { + conn, err := net.DialTimeout(network, addr, config.Timeout) + if err != nil { + return nil, err + } + c, chans, reqs, err := NewClientConn(conn, addr, config) + if err != nil { + return nil, err + } + return NewClient(c, chans, reqs), nil +} + +// A ClientConfig structure is used to configure a Client. It must not be +// modified after having been passed to an SSH function. +type ClientConfig struct { + // Config contains configuration that is shared between clients and + // servers. + Config + + // User contains the username to authenticate as. + User string + + // Auth contains possible authentication methods to use with the + // server. Only the first instance of a particular RFC 4252 method will + // be used during authentication. + Auth []AuthMethod + + // HostKeyCallback, if not nil, is called during the cryptographic + // handshake to validate the server's host key. A nil HostKeyCallback + // implies that all host keys are accepted. + HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + + // ClientVersion contains the version identification string that will + // be used for the connection. If empty, a reasonable default is used. + ClientVersion string + + // HostKeyAlgorithms lists the key types that the client will + // accept from the server as host key, in order of + // preference. If empty, a reasonable default is used. Any + // string returned from PublicKey.Type method may be used, or + // any of the CertAlgoXxxx and KeyAlgoXxxx constants. + HostKeyAlgorithms []string + + // Timeout is the maximum amount of time for the TCP connection to establish. + // + // A Timeout of zero means no timeout. + Timeout time.Duration +} diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go new file mode 100644 index 000000000..fd1ec5dda --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -0,0 +1,475 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" +) + +// clientAuthenticate authenticates with the remote server. See RFC 4252. +func (c *connection) clientAuthenticate(config *ClientConfig) error { + // initiate user auth session + if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil { + return err + } + packet, err := c.transport.readPacket() + if err != nil { + return err + } + var serviceAccept serviceAcceptMsg + if err := Unmarshal(packet, &serviceAccept); err != nil { + return err + } + + // during the authentication phase the client first attempts the "none" method + // then any untried methods suggested by the server. + tried := make(map[string]bool) + var lastMethods []string + + sessionID := c.transport.getSessionID() + for auth := AuthMethod(new(noneAuth)); auth != nil; { + ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand) + if err != nil { + return err + } + if ok { + // success + return nil + } + tried[auth.method()] = true + if methods == nil { + methods = lastMethods + } + lastMethods = methods + + auth = nil + + findNext: + for _, a := range config.Auth { + candidateMethod := a.method() + if tried[candidateMethod] { + continue + } + for _, meth := range methods { + if meth == candidateMethod { + auth = a + break findNext + } + } + } + } + return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried)) +} + +func keys(m map[string]bool) []string { + s := make([]string, 0, len(m)) + + for key := range m { + s = append(s, key) + } + return s +} + +// An AuthMethod represents an instance of an RFC 4252 authentication method. +type AuthMethod interface { + // auth authenticates user over transport t. + // Returns true if authentication is successful. + // If authentication is not successful, a []string of alternative + // method names is returned. If the slice is nil, it will be ignored + // and the previous set of possible methods will be reused. + auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error) + + // method returns the RFC 4252 method name. + method() string +} + +// "none" authentication, RFC 4252 section 5.2. +type noneAuth int + +func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { + if err := c.writePacket(Marshal(&userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: "none", + })); err != nil { + return false, nil, err + } + + return handleAuthResponse(c) +} + +func (n *noneAuth) method() string { + return "none" +} + +// passwordCallback is an AuthMethod that fetches the password through +// a function call, e.g. by prompting the user. +type passwordCallback func() (password string, err error) + +func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { + type passwordAuthMsg struct { + User string `sshtype:"50"` + Service string + Method string + Reply bool + Password string + } + + pw, err := cb() + // REVIEW NOTE: is there a need to support skipping a password attempt? + // The program may only find out that the user doesn't have a password + // when prompting. + if err != nil { + return false, nil, err + } + + if err := c.writePacket(Marshal(&passwordAuthMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + Reply: false, + Password: pw, + })); err != nil { + return false, nil, err + } + + return handleAuthResponse(c) +} + +func (cb passwordCallback) method() string { + return "password" +} + +// Password returns an AuthMethod using the given password. +func Password(secret string) AuthMethod { + return passwordCallback(func() (string, error) { return secret, nil }) +} + +// PasswordCallback returns an AuthMethod that uses a callback for +// fetching a password. +func PasswordCallback(prompt func() (secret string, err error)) AuthMethod { + return passwordCallback(prompt) +} + +type publickeyAuthMsg struct { + User string `sshtype:"50"` + Service string + Method string + // HasSig indicates to the receiver packet that the auth request is signed and + // should be used for authentication of the request. + HasSig bool + Algoname string + PubKey []byte + // Sig is tagged with "rest" so Marshal will exclude it during + // validateKey + Sig []byte `ssh:"rest"` +} + +// publicKeyCallback is an AuthMethod that uses a set of key +// pairs for authentication. +type publicKeyCallback func() ([]Signer, error) + +func (cb publicKeyCallback) method() string { + return "publickey" +} + +func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { + // Authentication is performed in two stages. The first stage sends an + // enquiry to test if each key is acceptable to the remote. The second + // stage attempts to authenticate with the valid keys obtained in the + // first stage. + + signers, err := cb() + if err != nil { + return false, nil, err + } + var validKeys []Signer + for _, signer := range signers { + if ok, err := validateKey(signer.PublicKey(), user, c); ok { + validKeys = append(validKeys, signer) + } else { + if err != nil { + return false, nil, err + } + } + } + + // methods that may continue if this auth is not successful. + var methods []string + for _, signer := range validKeys { + pub := signer.PublicKey() + + pubKey := pub.Marshal() + sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + }, []byte(pub.Type()), pubKey)) + if err != nil { + return false, nil, err + } + + // manually wrap the serialized signature in a string + s := Marshal(sign) + sig := make([]byte, stringLength(len(s))) + marshalString(sig, s) + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + HasSig: true, + Algoname: pub.Type(), + PubKey: pubKey, + Sig: sig, + } + p := Marshal(&msg) + if err := c.writePacket(p); err != nil { + return false, nil, err + } + var success bool + success, methods, err = handleAuthResponse(c) + if err != nil { + return false, nil, err + } + if success { + return success, methods, err + } + } + return false, methods, nil +} + +// validateKey validates the key provided is acceptable to the server. +func validateKey(key PublicKey, user string, c packetConn) (bool, error) { + pubKey := key.Marshal() + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: "publickey", + HasSig: false, + Algoname: key.Type(), + PubKey: pubKey, + } + if err := c.writePacket(Marshal(&msg)); err != nil { + return false, err + } + + return confirmKeyAck(key, c) +} + +func confirmKeyAck(key PublicKey, c packetConn) (bool, error) { + pubKey := key.Marshal() + algoname := key.Type() + + for { + packet, err := c.readPacket() + if err != nil { + return false, err + } + switch packet[0] { + case msgUserAuthBanner: + // TODO(gpaul): add callback to present the banner to the user + case msgUserAuthPubKeyOk: + var msg userAuthPubKeyOkMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, err + } + if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) { + return false, nil + } + return true, nil + case msgUserAuthFailure: + return false, nil + default: + return false, unexpectedMessageError(msgUserAuthSuccess, packet[0]) + } + } +} + +// PublicKeys returns an AuthMethod that uses the given key +// pairs. +func PublicKeys(signers ...Signer) AuthMethod { + return publicKeyCallback(func() ([]Signer, error) { return signers, nil }) +} + +// PublicKeysCallback returns an AuthMethod that runs the given +// function to obtain a list of key pairs. +func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod { + return publicKeyCallback(getSigners) +} + +// handleAuthResponse returns whether the preceding authentication request succeeded +// along with a list of remaining authentication methods to try next and +// an error if an unexpected response was received. +func handleAuthResponse(c packetConn) (bool, []string, error) { + for { + packet, err := c.readPacket() + if err != nil { + return false, nil, err + } + + switch packet[0] { + case msgUserAuthBanner: + // TODO: add callback to present the banner to the user + case msgUserAuthFailure: + var msg userAuthFailureMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, nil, err + } + return false, msg.Methods, nil + case msgUserAuthSuccess: + return true, nil, nil + default: + return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0]) + } + } +} + +// KeyboardInteractiveChallenge should print questions, optionally +// disabling echoing (e.g. for passwords), and return all the answers. +// Challenge may be called multiple times in a single session. After +// successful authentication, the server may send a challenge with no +// questions, for which the user and instruction messages should be +// printed. RFC 4256 section 3.3 details how the UI should behave for +// both CLI and GUI environments. +type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error) + +// KeyboardInteractive returns a AuthMethod using a prompt/response +// sequence controlled by the server. +func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod { + return challenge +} + +func (cb KeyboardInteractiveChallenge) method() string { + return "keyboard-interactive" +} + +func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { + type initiateMsg struct { + User string `sshtype:"50"` + Service string + Method string + Language string + Submethods string + } + + if err := c.writePacket(Marshal(&initiateMsg{ + User: user, + Service: serviceSSH, + Method: "keyboard-interactive", + })); err != nil { + return false, nil, err + } + + for { + packet, err := c.readPacket() + if err != nil { + return false, nil, err + } + + // like handleAuthResponse, but with less options. + switch packet[0] { + case msgUserAuthBanner: + // TODO: Print banners during userauth. + continue + case msgUserAuthInfoRequest: + // OK + case msgUserAuthFailure: + var msg userAuthFailureMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, nil, err + } + return false, msg.Methods, nil + case msgUserAuthSuccess: + return true, nil, nil + default: + return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0]) + } + + var msg userAuthInfoRequestMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, nil, err + } + + // Manually unpack the prompt/echo pairs. + rest := msg.Prompts + var prompts []string + var echos []bool + for i := 0; i < int(msg.NumPrompts); i++ { + prompt, r, ok := parseString(rest) + if !ok || len(r) == 0 { + return false, nil, errors.New("ssh: prompt format error") + } + prompts = append(prompts, string(prompt)) + echos = append(echos, r[0] != 0) + rest = r[1:] + } + + if len(rest) != 0 { + return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs") + } + + answers, err := cb(msg.User, msg.Instruction, prompts, echos) + if err != nil { + return false, nil, err + } + + if len(answers) != len(prompts) { + return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback") + } + responseLength := 1 + 4 + for _, a := range answers { + responseLength += stringLength(len(a)) + } + serialized := make([]byte, responseLength) + p := serialized + p[0] = msgUserAuthInfoResponse + p = p[1:] + p = marshalUint32(p, uint32(len(answers))) + for _, a := range answers { + p = marshalString(p, []byte(a)) + } + + if err := c.writePacket(serialized); err != nil { + return false, nil, err + } + } +} + +type retryableAuthMethod struct { + authMethod AuthMethod + maxTries int +} + +func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) { + for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ { + ok, methods, err = r.authMethod.auth(session, user, c, rand) + if ok || err != nil { // either success or error terminate + return ok, methods, err + } + } + return ok, methods, err +} + +func (r *retryableAuthMethod) method() string { + return r.authMethod.method() +} + +// RetryableAuthMethod is a decorator for other auth methods enabling them to +// be retried up to maxTries before considering that AuthMethod itself failed. +// If maxTries is <= 0, will retry indefinitely +// +// This is useful for interactive clients using challenge/response type +// authentication (e.g. Keyboard-Interactive, Password, etc) where the user +// could mistype their response resulting in the server issuing a +// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4 +// [keyboard-interactive]); Without this decorator, the non-retryable +// AuthMethod would be removed from future consideration, and never tried again +// (and so the user would never be able to retry their entry). +func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod { + return &retryableAuthMethod{authMethod: auth, maxTries: maxTries} +} diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go new file mode 100644 index 000000000..8656d0f85 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -0,0 +1,371 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto" + "crypto/rand" + "fmt" + "io" + "sync" + + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" +) + +// These are string constants in the SSH protocol. +const ( + compressionNone = "none" + serviceUserAuth = "ssh-userauth" + serviceSSH = "ssh-connection" +) + +// supportedCiphers specifies the supported ciphers in preference order. +var supportedCiphers = []string{ + "aes128-ctr", "aes192-ctr", "aes256-ctr", + "aes128-gcm@openssh.com", + "arcfour256", "arcfour128", +} + +// supportedKexAlgos specifies the supported key-exchange algorithms in +// preference order. +var supportedKexAlgos = []string{ + kexAlgoCurve25519SHA256, + // P384 and P521 are not constant-time yet, but since we don't + // reuse ephemeral keys, using them for ECDH should be OK. + kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, + kexAlgoDH14SHA1, kexAlgoDH1SHA1, +} + +// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods +// of authenticating servers) in preference order. +var supportedHostKeyAlgos = []string{ + CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, + CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01, + + KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, + KeyAlgoRSA, KeyAlgoDSA, + + KeyAlgoED25519, +} + +// supportedMACs specifies a default set of MAC algorithms in preference order. +// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed +// because they have reached the end of their useful life. +var supportedMACs = []string{ + "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", +} + +var supportedCompressions = []string{compressionNone} + +// hashFuncs keeps the mapping of supported algorithms to their respective +// hashes needed for signature verification. +var hashFuncs = map[string]crypto.Hash{ + KeyAlgoRSA: crypto.SHA1, + KeyAlgoDSA: crypto.SHA1, + KeyAlgoECDSA256: crypto.SHA256, + KeyAlgoECDSA384: crypto.SHA384, + KeyAlgoECDSA521: crypto.SHA512, + CertAlgoRSAv01: crypto.SHA1, + CertAlgoDSAv01: crypto.SHA1, + CertAlgoECDSA256v01: crypto.SHA256, + CertAlgoECDSA384v01: crypto.SHA384, + CertAlgoECDSA521v01: crypto.SHA512, +} + +// unexpectedMessageError results when the SSH message that we received didn't +// match what we wanted. +func unexpectedMessageError(expected, got uint8) error { + return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected) +} + +// parseError results from a malformed SSH message. +func parseError(tag uint8) error { + return fmt.Errorf("ssh: parse error in message type %d", tag) +} + +func findCommon(what string, client []string, server []string) (common string, err error) { + for _, c := range client { + for _, s := range server { + if c == s { + return c, nil + } + } + } + return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server) +} + +type directionAlgorithms struct { + Cipher string + MAC string + Compression string +} + +// rekeyBytes returns a rekeying intervals in bytes. +func (a *directionAlgorithms) rekeyBytes() int64 { + // According to RFC4344 block ciphers should rekey after + // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is + // 128. + switch a.Cipher { + case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID: + return 16 * (1 << 32) + + } + + // For others, stick with RFC4253 recommendation to rekey after 1 Gb of data. + return 1 << 30 +} + +type algorithms struct { + kex string + hostKey string + w directionAlgorithms + r directionAlgorithms +} + +func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) { + result := &algorithms{} + + result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos) + if err != nil { + return + } + + result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) + if err != nil { + return + } + + result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) + if err != nil { + return + } + + result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) + if err != nil { + return + } + + result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) + if err != nil { + return + } + + result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) + if err != nil { + return + } + + result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) + if err != nil { + return + } + + result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) + if err != nil { + return + } + + return result, nil +} + +// If rekeythreshold is too small, we can't make any progress sending +// stuff. +const minRekeyThreshold uint64 = 256 + +// Config contains configuration data common to both ServerConfig and +// ClientConfig. +type Config struct { + // Rand provides the source of entropy for cryptographic + // primitives. If Rand is nil, the cryptographic random reader + // in package crypto/rand will be used. + Rand io.Reader + + // The maximum number of bytes sent or received after which a + // new key is negotiated. It must be at least 256. If + // unspecified, 1 gigabyte is used. + RekeyThreshold uint64 + + // The allowed key exchanges algorithms. If unspecified then a + // default set of algorithms is used. + KeyExchanges []string + + // The allowed cipher algorithms. If unspecified then a sensible + // default is used. + Ciphers []string + + // The allowed MAC algorithms. If unspecified then a sensible default + // is used. + MACs []string +} + +// SetDefaults sets sensible values for unset fields in config. This is +// exported for testing: Configs passed to SSH functions are copied and have +// default values set automatically. +func (c *Config) SetDefaults() { + if c.Rand == nil { + c.Rand = rand.Reader + } + if c.Ciphers == nil { + c.Ciphers = supportedCiphers + } + var ciphers []string + for _, c := range c.Ciphers { + if cipherModes[c] != nil { + // reject the cipher if we have no cipherModes definition + ciphers = append(ciphers, c) + } + } + c.Ciphers = ciphers + + if c.KeyExchanges == nil { + c.KeyExchanges = supportedKexAlgos + } + + if c.MACs == nil { + c.MACs = supportedMACs + } + + if c.RekeyThreshold == 0 { + // RFC 4253, section 9 suggests rekeying after 1G. + c.RekeyThreshold = 1 << 30 + } + if c.RekeyThreshold < minRekeyThreshold { + c.RekeyThreshold = minRekeyThreshold + } +} + +// buildDataSignedForAuth returns the data that is signed in order to prove +// possession of a private key. See RFC 4252, section 7. +func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { + data := struct { + Session []byte + Type byte + User string + Service string + Method string + Sign bool + Algo []byte + PubKey []byte + }{ + sessionId, + msgUserAuthRequest, + req.User, + req.Service, + req.Method, + true, + algo, + pubKey, + } + return Marshal(data) +} + +func appendU16(buf []byte, n uint16) []byte { + return append(buf, byte(n>>8), byte(n)) +} + +func appendU32(buf []byte, n uint32) []byte { + return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) +} + +func appendU64(buf []byte, n uint64) []byte { + return append(buf, + byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), + byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) +} + +func appendInt(buf []byte, n int) []byte { + return appendU32(buf, uint32(n)) +} + +func appendString(buf []byte, s string) []byte { + buf = appendU32(buf, uint32(len(s))) + buf = append(buf, s...) + return buf +} + +func appendBool(buf []byte, b bool) []byte { + if b { + return append(buf, 1) + } + return append(buf, 0) +} + +// newCond is a helper to hide the fact that there is no usable zero +// value for sync.Cond. +func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } + +// window represents the buffer available to clients +// wishing to write to a channel. +type window struct { + *sync.Cond + win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 + writeWaiters int + closed bool +} + +// add adds win to the amount of window available +// for consumers. +func (w *window) add(win uint32) bool { + // a zero sized window adjust is a noop. + if win == 0 { + return true + } + w.L.Lock() + if w.win+win < win { + w.L.Unlock() + return false + } + w.win += win + // It is unusual that multiple goroutines would be attempting to reserve + // window space, but not guaranteed. Use broadcast to notify all waiters + // that additional window is available. + w.Broadcast() + w.L.Unlock() + return true +} + +// close sets the window to closed, so all reservations fail +// immediately. +func (w *window) close() { + w.L.Lock() + w.closed = true + w.Broadcast() + w.L.Unlock() +} + +// reserve reserves win from the available window capacity. +// If no capacity remains, reserve will block. reserve may +// return less than requested. +func (w *window) reserve(win uint32) (uint32, error) { + var err error + w.L.Lock() + w.writeWaiters++ + w.Broadcast() + for w.win == 0 && !w.closed { + w.Wait() + } + w.writeWaiters-- + if w.win < win { + win = w.win + } + w.win -= win + if w.closed { + err = io.EOF + } + w.L.Unlock() + return win, err +} + +// waitWriterBlocked waits until some goroutine is blocked for further +// writes. It is used in tests only. +func (w *window) waitWriterBlocked() { + w.Cond.L.Lock() + for w.writeWaiters == 0 { + w.Cond.Wait() + } + w.Cond.L.Unlock() +} diff --git a/vendor/golang.org/x/crypto/ssh/connection.go b/vendor/golang.org/x/crypto/ssh/connection.go new file mode 100644 index 000000000..e786f2f9a --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/connection.go @@ -0,0 +1,143 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "fmt" + "net" +) + +// OpenChannelError is returned if the other side rejects an +// OpenChannel request. +type OpenChannelError struct { + Reason RejectionReason + Message string +} + +func (e *OpenChannelError) Error() string { + return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message) +} + +// ConnMetadata holds metadata for the connection. +type ConnMetadata interface { + // User returns the user ID for this connection. + User() string + + // SessionID returns the sesson hash, also denoted by H. + SessionID() []byte + + // ClientVersion returns the client's version string as hashed + // into the session ID. + ClientVersion() []byte + + // ServerVersion returns the server's version string as hashed + // into the session ID. + ServerVersion() []byte + + // RemoteAddr returns the remote address for this connection. + RemoteAddr() net.Addr + + // LocalAddr returns the local address for this connection. + LocalAddr() net.Addr +} + +// Conn represents an SSH connection for both server and client roles. +// Conn is the basis for implementing an application layer, such +// as ClientConn, which implements the traditional shell access for +// clients. +type Conn interface { + ConnMetadata + + // SendRequest sends a global request, and returns the + // reply. If wantReply is true, it returns the response status + // and payload. See also RFC4254, section 4. + SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) + + // OpenChannel tries to open an channel. If the request is + // rejected, it returns *OpenChannelError. On success it returns + // the SSH Channel and a Go channel for incoming, out-of-band + // requests. The Go channel must be serviced, or the + // connection will hang. + OpenChannel(name string, data []byte) (Channel, <-chan *Request, error) + + // Close closes the underlying network connection + Close() error + + // Wait blocks until the connection has shut down, and returns the + // error causing the shutdown. + Wait() error + + // TODO(hanwen): consider exposing: + // RequestKeyChange + // Disconnect +} + +// DiscardRequests consumes and rejects all requests from the +// passed-in channel. +func DiscardRequests(in <-chan *Request) { + for req := range in { + if req.WantReply { + req.Reply(false, nil) + } + } +} + +// A connection represents an incoming connection. +type connection struct { + transport *handshakeTransport + sshConn + + // The connection protocol. + *mux +} + +func (c *connection) Close() error { + return c.sshConn.conn.Close() +} + +// sshconn provides net.Conn metadata, but disallows direct reads and +// writes. +type sshConn struct { + conn net.Conn + + user string + sessionID []byte + clientVersion []byte + serverVersion []byte +} + +func dup(src []byte) []byte { + dst := make([]byte, len(src)) + copy(dst, src) + return dst +} + +func (c *sshConn) User() string { + return c.user +} + +func (c *sshConn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +func (c *sshConn) Close() error { + return c.conn.Close() +} + +func (c *sshConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *sshConn) SessionID() []byte { + return dup(c.sessionID) +} + +func (c *sshConn) ClientVersion() []byte { + return dup(c.clientVersion) +} + +func (c *sshConn) ServerVersion() []byte { + return dup(c.serverVersion) +} diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go new file mode 100644 index 000000000..d6be89466 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/doc.go @@ -0,0 +1,18 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package ssh implements an SSH client and server. + +SSH is a transport security protocol, an authentication protocol and a +family of application protocols. The most typical application level +protocol is a remote shell and this is specifically implemented. However, +the multiplexed nature of SSH is exposed to users that wish to support +others. + +References: + [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD + [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 +*/ +package ssh // import "golang.org/x/crypto/ssh" diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go new file mode 100644 index 000000000..8de650644 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -0,0 +1,625 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto/rand" + "errors" + "fmt" + "io" + "log" + "net" + "sync" +) + +// debugHandshake, if set, prints messages sent and received. Key +// exchange messages are printed as if DH were used, so the debug +// messages are wrong when using ECDH. +const debugHandshake = false + +// chanSize sets the amount of buffering SSH connections. This is +// primarily for testing: setting chanSize=0 uncovers deadlocks more +// quickly. +const chanSize = 16 + +// keyingTransport is a packet based transport that supports key +// changes. It need not be thread-safe. It should pass through +// msgNewKeys in both directions. +type keyingTransport interface { + packetConn + + // prepareKeyChange sets up a key change. The key change for a + // direction will be effected if a msgNewKeys message is sent + // or received. + prepareKeyChange(*algorithms, *kexResult) error +} + +// handshakeTransport implements rekeying on top of a keyingTransport +// and offers a thread-safe writePacket() interface. +type handshakeTransport struct { + conn keyingTransport + config *Config + + serverVersion []byte + clientVersion []byte + + // hostKeys is non-empty if we are the server. In that case, + // it contains all host keys that can be used to sign the + // connection. + hostKeys []Signer + + // hostKeyAlgorithms is non-empty if we are the client. In that case, + // we accept these key types from the server as host key. + hostKeyAlgorithms []string + + // On read error, incoming is closed, and readError is set. + incoming chan []byte + readError error + + mu sync.Mutex + writeError error + sentInitPacket []byte + sentInitMsg *kexInitMsg + pendingPackets [][]byte // Used when a key exchange is in progress. + + // If the read loop wants to schedule a kex, it pings this + // channel, and the write loop will send out a kex + // message. + requestKex chan struct{} + + // If the other side requests or confirms a kex, its kexInit + // packet is sent here for the write loop to find it. + startKex chan *pendingKex + + // data for host key checking + hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + dialAddress string + remoteAddr net.Addr + + // Algorithms agreed in the last key exchange. + algorithms *algorithms + + readPacketsLeft uint32 + readBytesLeft int64 + + writePacketsLeft uint32 + writeBytesLeft int64 + + // The session ID or nil if first kex did not complete yet. + sessionID []byte +} + +type pendingKex struct { + otherInit []byte + done chan error +} + +func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { + t := &handshakeTransport{ + conn: conn, + serverVersion: serverVersion, + clientVersion: clientVersion, + incoming: make(chan []byte, chanSize), + requestKex: make(chan struct{}, 1), + startKex: make(chan *pendingKex, 1), + + config: config, + } + + // We always start with a mandatory key exchange. + t.requestKex <- struct{}{} + return t +} + +func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ClientConfig, dialAddr string, addr net.Addr) *handshakeTransport { + t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) + t.dialAddress = dialAddr + t.remoteAddr = addr + t.hostKeyCallback = config.HostKeyCallback + if config.HostKeyAlgorithms != nil { + t.hostKeyAlgorithms = config.HostKeyAlgorithms + } else { + t.hostKeyAlgorithms = supportedHostKeyAlgos + } + go t.readLoop() + go t.kexLoop() + return t +} + +func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport { + t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) + t.hostKeys = config.hostKeys + go t.readLoop() + go t.kexLoop() + return t +} + +func (t *handshakeTransport) getSessionID() []byte { + return t.sessionID +} + +// waitSession waits for the session to be established. This should be +// the first thing to call after instantiating handshakeTransport. +func (t *handshakeTransport) waitSession() error { + p, err := t.readPacket() + if err != nil { + return err + } + if p[0] != msgNewKeys { + return fmt.Errorf("ssh: first packet should be msgNewKeys") + } + + return nil +} + +func (t *handshakeTransport) id() string { + if len(t.hostKeys) > 0 { + return "server" + } + return "client" +} + +func (t *handshakeTransport) printPacket(p []byte, write bool) { + action := "got" + if write { + action = "sent" + } + + if p[0] == msgChannelData || p[0] == msgChannelExtendedData { + log.Printf("%s %s data (packet %d bytes)", t.id(), action, len(p)) + } else { + msg, err := decode(p) + log.Printf("%s %s %T %v (%v)", t.id(), action, msg, msg, err) + } +} + +func (t *handshakeTransport) readPacket() ([]byte, error) { + p, ok := <-t.incoming + if !ok { + return nil, t.readError + } + return p, nil +} + +func (t *handshakeTransport) readLoop() { + first := true + for { + p, err := t.readOnePacket(first) + first = false + if err != nil { + t.readError = err + close(t.incoming) + break + } + if p[0] == msgIgnore || p[0] == msgDebug { + continue + } + t.incoming <- p + } + + // Stop writers too. + t.recordWriteError(t.readError) + + // Unblock the writer should it wait for this. + close(t.startKex) + + // Don't close t.requestKex; it's also written to from writePacket. +} + +func (t *handshakeTransport) pushPacket(p []byte) error { + if debugHandshake { + t.printPacket(p, true) + } + return t.conn.writePacket(p) +} + +func (t *handshakeTransport) getWriteError() error { + t.mu.Lock() + defer t.mu.Unlock() + return t.writeError +} + +func (t *handshakeTransport) recordWriteError(err error) { + t.mu.Lock() + defer t.mu.Unlock() + if t.writeError == nil && err != nil { + t.writeError = err + } +} + +func (t *handshakeTransport) requestKeyExchange() { + select { + case t.requestKex <- struct{}{}: + default: + // something already requested a kex, so do nothing. + } +} + +func (t *handshakeTransport) kexLoop() { + +write: + for t.getWriteError() == nil { + var request *pendingKex + var sent bool + + for request == nil || !sent { + var ok bool + select { + case request, ok = <-t.startKex: + if !ok { + break write + } + case <-t.requestKex: + break + } + + if !sent { + if err := t.sendKexInit(); err != nil { + t.recordWriteError(err) + break + } + sent = true + } + } + + if err := t.getWriteError(); err != nil { + if request != nil { + request.done <- err + } + break + } + + // We're not servicing t.requestKex, but that is OK: + // we never block on sending to t.requestKex. + + // We're not servicing t.startKex, but the remote end + // has just sent us a kexInitMsg, so it can't send + // another key change request, until we close the done + // channel on the pendingKex request. + + err := t.enterKeyExchange(request.otherInit) + + t.mu.Lock() + t.writeError = err + t.sentInitPacket = nil + t.sentInitMsg = nil + t.writePacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.writeBytesLeft = int64(t.config.RekeyThreshold) + } else if t.algorithms != nil { + t.writeBytesLeft = t.algorithms.w.rekeyBytes() + } + + // we have completed the key exchange. Since the + // reader is still blocked, it is safe to clear out + // the requestKex channel. This avoids the situation + // where: 1) we consumed our own request for the + // initial kex, and 2) the kex from the remote side + // caused another send on the requestKex channel, + clear: + for { + select { + case <-t.requestKex: + // + default: + break clear + } + } + + request.done <- t.writeError + + // kex finished. Push packets that we received while + // the kex was in progress. Don't look at t.startKex + // and don't increment writtenSinceKex: if we trigger + // another kex while we are still busy with the last + // one, things will become very confusing. + for _, p := range t.pendingPackets { + t.writeError = t.pushPacket(p) + if t.writeError != nil { + break + } + } + t.pendingPackets = t.pendingPackets[:0] + t.mu.Unlock() + } + + // drain startKex channel. We don't service t.requestKex + // because nobody does blocking sends there. + go func() { + for init := range t.startKex { + init.done <- t.writeError + } + }() + + // Unblock reader. + t.conn.Close() +} + +// The protocol uses uint32 for packet counters, so we can't let them +// reach 1<<32. We will actually read and write more packets than +// this, though: the other side may send more packets, and after we +// hit this limit on writing we will send a few more packets for the +// key exchange itself. +const packetRekeyThreshold = (1 << 31) + +func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { + p, err := t.conn.readPacket() + if err != nil { + return nil, err + } + + if t.readPacketsLeft > 0 { + t.readPacketsLeft-- + } else { + t.requestKeyExchange() + } + + if t.readBytesLeft > 0 { + t.readBytesLeft -= int64(len(p)) + } else { + t.requestKeyExchange() + } + + if debugHandshake { + t.printPacket(p, false) + } + + if first && p[0] != msgKexInit { + return nil, fmt.Errorf("ssh: first packet should be msgKexInit") + } + + if p[0] != msgKexInit { + return p, nil + } + + firstKex := t.sessionID == nil + + kex := pendingKex{ + done: make(chan error, 1), + otherInit: p, + } + t.startKex <- &kex + err = <-kex.done + + if debugHandshake { + log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err) + } + + if err != nil { + return nil, err + } + + t.readPacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.readBytesLeft = int64(t.config.RekeyThreshold) + } else { + t.readBytesLeft = t.algorithms.r.rekeyBytes() + } + + // By default, a key exchange is hidden from higher layers by + // translating it into msgIgnore. + successPacket := []byte{msgIgnore} + if firstKex { + // sendKexInit() for the first kex waits for + // msgNewKeys so the authentication process is + // guaranteed to happen over an encrypted transport. + successPacket = []byte{msgNewKeys} + } + + return successPacket, nil +} + +// sendKexInit sends a key change message. +func (t *handshakeTransport) sendKexInit() error { + t.mu.Lock() + defer t.mu.Unlock() + if t.sentInitMsg != nil { + // kexInits may be sent either in response to the other side, + // or because our side wants to initiate a key change, so we + // may have already sent a kexInit. In that case, don't send a + // second kexInit. + return nil + } + + msg := &kexInitMsg{ + KexAlgos: t.config.KeyExchanges, + CiphersClientServer: t.config.Ciphers, + CiphersServerClient: t.config.Ciphers, + MACsClientServer: t.config.MACs, + MACsServerClient: t.config.MACs, + CompressionClientServer: supportedCompressions, + CompressionServerClient: supportedCompressions, + } + io.ReadFull(rand.Reader, msg.Cookie[:]) + + if len(t.hostKeys) > 0 { + for _, k := range t.hostKeys { + msg.ServerHostKeyAlgos = append( + msg.ServerHostKeyAlgos, k.PublicKey().Type()) + } + } else { + msg.ServerHostKeyAlgos = t.hostKeyAlgorithms + } + packet := Marshal(msg) + + // writePacket destroys the contents, so save a copy. + packetCopy := make([]byte, len(packet)) + copy(packetCopy, packet) + + if err := t.pushPacket(packetCopy); err != nil { + return err + } + + t.sentInitMsg = msg + t.sentInitPacket = packet + + return nil +} + +func (t *handshakeTransport) writePacket(p []byte) error { + switch p[0] { + case msgKexInit: + return errors.New("ssh: only handshakeTransport can send kexInit") + case msgNewKeys: + return errors.New("ssh: only handshakeTransport can send newKeys") + } + + t.mu.Lock() + defer t.mu.Unlock() + if t.writeError != nil { + return t.writeError + } + + if t.sentInitMsg != nil { + // Copy the packet so the writer can reuse the buffer. + cp := make([]byte, len(p)) + copy(cp, p) + t.pendingPackets = append(t.pendingPackets, cp) + return nil + } + + if t.writeBytesLeft > 0 { + t.writeBytesLeft -= int64(len(p)) + } else { + t.requestKeyExchange() + } + + if t.writePacketsLeft > 0 { + t.writePacketsLeft-- + } else { + t.requestKeyExchange() + } + + if err := t.pushPacket(p); err != nil { + t.writeError = err + } + + return nil +} + +func (t *handshakeTransport) Close() error { + return t.conn.Close() +} + +func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { + if debugHandshake { + log.Printf("%s entered key exchange", t.id()) + } + + otherInit := &kexInitMsg{} + if err := Unmarshal(otherInitPacket, otherInit); err != nil { + return err + } + + magics := handshakeMagics{ + clientVersion: t.clientVersion, + serverVersion: t.serverVersion, + clientKexInit: otherInitPacket, + serverKexInit: t.sentInitPacket, + } + + clientInit := otherInit + serverInit := t.sentInitMsg + if len(t.hostKeys) == 0 { + clientInit, serverInit = serverInit, clientInit + + magics.clientKexInit = t.sentInitPacket + magics.serverKexInit = otherInitPacket + } + + var err error + t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit) + if err != nil { + return err + } + + // We don't send FirstKexFollows, but we handle receiving it. + // + // RFC 4253 section 7 defines the kex and the agreement method for + // first_kex_packet_follows. It states that the guessed packet + // should be ignored if the "kex algorithm and/or the host + // key algorithm is guessed wrong (server and client have + // different preferred algorithm), or if any of the other + // algorithms cannot be agreed upon". The other algorithms have + // already been checked above so the kex algorithm and host key + // algorithm are checked here. + if otherInit.FirstKexFollows && (clientInit.KexAlgos[0] != serverInit.KexAlgos[0] || clientInit.ServerHostKeyAlgos[0] != serverInit.ServerHostKeyAlgos[0]) { + // other side sent a kex message for the wrong algorithm, + // which we have to ignore. + if _, err := t.conn.readPacket(); err != nil { + return err + } + } + + kex, ok := kexAlgoMap[t.algorithms.kex] + if !ok { + return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex) + } + + var result *kexResult + if len(t.hostKeys) > 0 { + result, err = t.server(kex, t.algorithms, &magics) + } else { + result, err = t.client(kex, t.algorithms, &magics) + } + + if err != nil { + return err + } + + if t.sessionID == nil { + t.sessionID = result.H + } + result.SessionID = t.sessionID + + t.conn.prepareKeyChange(t.algorithms, result) + if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { + return err + } + if packet, err := t.conn.readPacket(); err != nil { + return err + } else if packet[0] != msgNewKeys { + return unexpectedMessageError(msgNewKeys, packet[0]) + } + + return nil +} + +func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { + var hostKey Signer + for _, k := range t.hostKeys { + if algs.hostKey == k.PublicKey().Type() { + hostKey = k + } + } + + r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey) + return r, err +} + +func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { + result, err := kex.Client(t.conn, t.config.Rand, magics) + if err != nil { + return nil, err + } + + hostKey, err := ParsePublicKey(result.HostKey) + if err != nil { + return nil, err + } + + if err := verifyHostKeySignature(hostKey, result); err != nil { + return nil, err + } + + if t.hostKeyCallback != nil { + err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) + if err != nil { + return nil, err + } + } + + return result, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go new file mode 100644 index 000000000..c87fbebfd --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -0,0 +1,540 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/subtle" + "errors" + "io" + "math/big" + + "golang.org/x/crypto/curve25519" +) + +const ( + kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" + kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" + kexAlgoECDH256 = "ecdh-sha2-nistp256" + kexAlgoECDH384 = "ecdh-sha2-nistp384" + kexAlgoECDH521 = "ecdh-sha2-nistp521" + kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org" +) + +// kexResult captures the outcome of a key exchange. +type kexResult struct { + // Session hash. See also RFC 4253, section 8. + H []byte + + // Shared secret. See also RFC 4253, section 8. + K []byte + + // Host key as hashed into H. + HostKey []byte + + // Signature of H. + Signature []byte + + // A cryptographic hash function that matches the security + // level of the key exchange algorithm. It is used for + // calculating H, and for deriving keys from H and K. + Hash crypto.Hash + + // The session ID, which is the first H computed. This is used + // to derive key material inside the transport. + SessionID []byte +} + +// handshakeMagics contains data that is always included in the +// session hash. +type handshakeMagics struct { + clientVersion, serverVersion []byte + clientKexInit, serverKexInit []byte +} + +func (m *handshakeMagics) write(w io.Writer) { + writeString(w, m.clientVersion) + writeString(w, m.serverVersion) + writeString(w, m.clientKexInit) + writeString(w, m.serverKexInit) +} + +// kexAlgorithm abstracts different key exchange algorithms. +type kexAlgorithm interface { + // Server runs server-side key agreement, signing the result + // with a hostkey. + Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) + + // Client runs the client-side key agreement. Caller is + // responsible for verifying the host key signature. + Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) +} + +// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. +type dhGroup struct { + g, p, pMinus1 *big.Int +} + +func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { + if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 { + return nil, errors.New("ssh: DH parameter out of bounds") + } + return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil +} + +func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { + hashFunc := crypto.SHA1 + + var x *big.Int + for { + var err error + if x, err = rand.Int(randSource, group.pMinus1); err != nil { + return nil, err + } + if x.Sign() > 0 { + break + } + } + + X := new(big.Int).Exp(group.g, x, group.p) + kexDHInit := kexDHInitMsg{ + X: X, + } + if err := c.writePacket(Marshal(&kexDHInit)); err != nil { + return nil, err + } + + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var kexDHReply kexDHReplyMsg + if err = Unmarshal(packet, &kexDHReply); err != nil { + return nil, err + } + + kInt, err := group.diffieHellman(kexDHReply.Y, x) + if err != nil { + return nil, err + } + + h := hashFunc.New() + magics.write(h) + writeString(h, kexDHReply.HostKey) + writeInt(h, X) + writeInt(h, kexDHReply.Y) + K := make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: kexDHReply.HostKey, + Signature: kexDHReply.Signature, + Hash: crypto.SHA1, + }, nil +} + +func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + hashFunc := crypto.SHA1 + packet, err := c.readPacket() + if err != nil { + return + } + var kexDHInit kexDHInitMsg + if err = Unmarshal(packet, &kexDHInit); err != nil { + return + } + + var y *big.Int + for { + if y, err = rand.Int(randSource, group.pMinus1); err != nil { + return + } + if y.Sign() > 0 { + break + } + } + + Y := new(big.Int).Exp(group.g, y, group.p) + kInt, err := group.diffieHellman(kexDHInit.X, y) + if err != nil { + return nil, err + } + + hostKeyBytes := priv.PublicKey().Marshal() + + h := hashFunc.New() + magics.write(h) + writeString(h, hostKeyBytes) + writeInt(h, kexDHInit.X) + writeInt(h, Y) + + K := make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + H := h.Sum(nil) + + // H is already a hash, but the hostkey signing will apply its + // own key-specific hash algorithm. + sig, err := signAndMarshal(priv, randSource, H) + if err != nil { + return nil, err + } + + kexDHReply := kexDHReplyMsg{ + HostKey: hostKeyBytes, + Y: Y, + Signature: sig, + } + packet = Marshal(&kexDHReply) + + err = c.writePacket(packet) + return &kexResult{ + H: H, + K: K, + HostKey: hostKeyBytes, + Signature: sig, + Hash: crypto.SHA1, + }, nil +} + +// ecdh performs Elliptic Curve Diffie-Hellman key exchange as +// described in RFC 5656, section 4. +type ecdh struct { + curve elliptic.Curve +} + +func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { + ephKey, err := ecdsa.GenerateKey(kex.curve, rand) + if err != nil { + return nil, err + } + + kexInit := kexECDHInitMsg{ + ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), + } + + serialized := Marshal(&kexInit) + if err := c.writePacket(serialized); err != nil { + return nil, err + } + + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var reply kexECDHReplyMsg + if err = Unmarshal(packet, &reply); err != nil { + return nil, err + } + + x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey) + if err != nil { + return nil, err + } + + // generate shared secret + secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes()) + + h := ecHash(kex.curve).New() + magics.write(h) + writeString(h, reply.HostKey) + writeString(h, kexInit.ClientPubKey) + writeString(h, reply.EphemeralPubKey) + K := make([]byte, intLength(secret)) + marshalInt(K, secret) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: reply.HostKey, + Signature: reply.Signature, + Hash: ecHash(kex.curve), + }, nil +} + +// unmarshalECKey parses and checks an EC key. +func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) { + x, y = elliptic.Unmarshal(curve, pubkey) + if x == nil { + return nil, nil, errors.New("ssh: elliptic.Unmarshal failure") + } + if !validateECPublicKey(curve, x, y) { + return nil, nil, errors.New("ssh: public key not on curve") + } + return x, y, nil +} + +// validateECPublicKey checks that the point is a valid public key for +// the given curve. See [SEC1], 3.2.2 +func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { + if x.Sign() == 0 && y.Sign() == 0 { + return false + } + + if x.Cmp(curve.Params().P) >= 0 { + return false + } + + if y.Cmp(curve.Params().P) >= 0 { + return false + } + + if !curve.IsOnCurve(x, y) { + return false + } + + // We don't check if N * PubKey == 0, since + // + // - the NIST curves have cofactor = 1, so this is implicit. + // (We don't foresee an implementation that supports non NIST + // curves) + // + // - for ephemeral keys, we don't need to worry about small + // subgroup attacks. + return true +} + +func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var kexECDHInit kexECDHInitMsg + if err = Unmarshal(packet, &kexECDHInit); err != nil { + return nil, err + } + + clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey) + if err != nil { + return nil, err + } + + // We could cache this key across multiple users/multiple + // connection attempts, but the benefit is small. OpenSSH + // generates a new key for each incoming connection. + ephKey, err := ecdsa.GenerateKey(kex.curve, rand) + if err != nil { + return nil, err + } + + hostKeyBytes := priv.PublicKey().Marshal() + + serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y) + + // generate shared secret + secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) + + h := ecHash(kex.curve).New() + magics.write(h) + writeString(h, hostKeyBytes) + writeString(h, kexECDHInit.ClientPubKey) + writeString(h, serializedEphKey) + + K := make([]byte, intLength(secret)) + marshalInt(K, secret) + h.Write(K) + + H := h.Sum(nil) + + // H is already a hash, but the hostkey signing will apply its + // own key-specific hash algorithm. + sig, err := signAndMarshal(priv, rand, H) + if err != nil { + return nil, err + } + + reply := kexECDHReplyMsg{ + EphemeralPubKey: serializedEphKey, + HostKey: hostKeyBytes, + Signature: sig, + } + + serialized := Marshal(&reply) + if err := c.writePacket(serialized); err != nil { + return nil, err + } + + return &kexResult{ + H: H, + K: K, + HostKey: reply.HostKey, + Signature: sig, + Hash: ecHash(kex.curve), + }, nil +} + +var kexAlgoMap = map[string]kexAlgorithm{} + +func init() { + // This is the group called diffie-hellman-group1-sha1 in RFC + // 4253 and Oakley Group 2 in RFC 2409. + p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) + kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + pMinus1: new(big.Int).Sub(p, bigOne), + } + + // This is the group called diffie-hellman-group14-sha1 in RFC + // 4253 and Oakley Group 14 in RFC 3526. + p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) + + kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + pMinus1: new(big.Int).Sub(p, bigOne), + } + + kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} + kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} + kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} + kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{} +} + +// curve25519sha256 implements the curve25519-sha256@libssh.org key +// agreement protocol, as described in +// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt +type curve25519sha256 struct{} + +type curve25519KeyPair struct { + priv [32]byte + pub [32]byte +} + +func (kp *curve25519KeyPair) generate(rand io.Reader) error { + if _, err := io.ReadFull(rand, kp.priv[:]); err != nil { + return err + } + curve25519.ScalarBaseMult(&kp.pub, &kp.priv) + return nil +} + +// curve25519Zeros is just an array of 32 zero bytes so that we have something +// convenient to compare against in order to reject curve25519 points with the +// wrong order. +var curve25519Zeros [32]byte + +func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { + var kp curve25519KeyPair + if err := kp.generate(rand); err != nil { + return nil, err + } + if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil { + return nil, err + } + + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var reply kexECDHReplyMsg + if err = Unmarshal(packet, &reply); err != nil { + return nil, err + } + if len(reply.EphemeralPubKey) != 32 { + return nil, errors.New("ssh: peer's curve25519 public value has wrong length") + } + + var servPub, secret [32]byte + copy(servPub[:], reply.EphemeralPubKey) + curve25519.ScalarMult(&secret, &kp.priv, &servPub) + if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { + return nil, errors.New("ssh: peer's curve25519 public value has wrong order") + } + + h := crypto.SHA256.New() + magics.write(h) + writeString(h, reply.HostKey) + writeString(h, kp.pub[:]) + writeString(h, reply.EphemeralPubKey) + + kInt := new(big.Int).SetBytes(secret[:]) + K := make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: reply.HostKey, + Signature: reply.Signature, + Hash: crypto.SHA256, + }, nil +} + +func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + packet, err := c.readPacket() + if err != nil { + return + } + var kexInit kexECDHInitMsg + if err = Unmarshal(packet, &kexInit); err != nil { + return + } + + if len(kexInit.ClientPubKey) != 32 { + return nil, errors.New("ssh: peer's curve25519 public value has wrong length") + } + + var kp curve25519KeyPair + if err := kp.generate(rand); err != nil { + return nil, err + } + + var clientPub, secret [32]byte + copy(clientPub[:], kexInit.ClientPubKey) + curve25519.ScalarMult(&secret, &kp.priv, &clientPub) + if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { + return nil, errors.New("ssh: peer's curve25519 public value has wrong order") + } + + hostKeyBytes := priv.PublicKey().Marshal() + + h := crypto.SHA256.New() + magics.write(h) + writeString(h, hostKeyBytes) + writeString(h, kexInit.ClientPubKey) + writeString(h, kp.pub[:]) + + kInt := new(big.Int).SetBytes(secret[:]) + K := make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + H := h.Sum(nil) + + sig, err := signAndMarshal(priv, rand, H) + if err != nil { + return nil, err + } + + reply := kexECDHReplyMsg{ + EphemeralPubKey: kp.pub[:], + HostKey: hostKeyBytes, + Signature: sig, + } + if err := c.writePacket(Marshal(&reply)); err != nil { + return nil, err + } + return &kexResult{ + H: H, + K: K, + HostKey: hostKeyBytes, + Signature: sig, + Hash: crypto.SHA256, + }, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go new file mode 100644 index 000000000..f38de9898 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -0,0 +1,905 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/md5" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "encoding/base64" + "encoding/hex" + "encoding/pem" + "errors" + "fmt" + "io" + "math/big" + "strings" + + "golang.org/x/crypto/ed25519" +) + +// These constants represent the algorithm names for key types supported by this +// package. +const ( + KeyAlgoRSA = "ssh-rsa" + KeyAlgoDSA = "ssh-dss" + KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" + KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" + KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" + KeyAlgoED25519 = "ssh-ed25519" +) + +// parsePubKey parses a public key of the given algorithm. +// Use ParsePublicKey for keys with prepended algorithm. +func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { + switch algo { + case KeyAlgoRSA: + return parseRSA(in) + case KeyAlgoDSA: + return parseDSA(in) + case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: + return parseECDSA(in) + case KeyAlgoED25519: + return parseED25519(in) + case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01: + cert, err := parseCert(in, certToPrivAlgo(algo)) + if err != nil { + return nil, nil, err + } + return cert, nil, nil + } + return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo) +} + +// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format +// (see sshd(8) manual page) once the options and key type fields have been +// removed. +func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) { + in = bytes.TrimSpace(in) + + i := bytes.IndexAny(in, " \t") + if i == -1 { + i = len(in) + } + base64Key := in[:i] + + key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) + n, err := base64.StdEncoding.Decode(key, base64Key) + if err != nil { + return nil, "", err + } + key = key[:n] + out, err = ParsePublicKey(key) + if err != nil { + return nil, "", err + } + comment = string(bytes.TrimSpace(in[i:])) + return out, comment, nil +} + +// ParseKnownHosts parses an entry in the format of the known_hosts file. +// +// The known_hosts format is documented in the sshd(8) manual page. This +// function will parse a single entry from in. On successful return, marker +// will contain the optional marker value (i.e. "cert-authority" or "revoked") +// or else be empty, hosts will contain the hosts that this entry matches, +// pubKey will contain the public key and comment will contain any trailing +// comment at the end of the line. See the sshd(8) manual page for the various +// forms that a host string can take. +// +// The unparsed remainder of the input will be returned in rest. This function +// can be called repeatedly to parse multiple entries. +// +// If no entries were found in the input then err will be io.EOF. Otherwise a +// non-nil err value indicates a parse error. +func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error) { + for len(in) > 0 { + end := bytes.IndexByte(in, '\n') + if end != -1 { + rest = in[end+1:] + in = in[:end] + } else { + rest = nil + } + + end = bytes.IndexByte(in, '\r') + if end != -1 { + in = in[:end] + } + + in = bytes.TrimSpace(in) + if len(in) == 0 || in[0] == '#' { + in = rest + continue + } + + i := bytes.IndexAny(in, " \t") + if i == -1 { + in = rest + continue + } + + // Strip out the beginning of the known_host key. + // This is either an optional marker or a (set of) hostname(s). + keyFields := bytes.Fields(in) + if len(keyFields) < 3 || len(keyFields) > 5 { + return "", nil, nil, "", nil, errors.New("ssh: invalid entry in known_hosts data") + } + + // keyFields[0] is either "@cert-authority", "@revoked" or a comma separated + // list of hosts + marker := "" + if keyFields[0][0] == '@' { + marker = string(keyFields[0][1:]) + keyFields = keyFields[1:] + } + + hosts := string(keyFields[0]) + // keyFields[1] contains the key type (e.g. “ssh-rsa”). + // However, that information is duplicated inside the + // base64-encoded key and so is ignored here. + + key := bytes.Join(keyFields[2:], []byte(" ")) + if pubKey, comment, err = parseAuthorizedKey(key); err != nil { + return "", nil, nil, "", nil, err + } + + return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil + } + + return "", nil, nil, "", nil, io.EOF +} + +// ParseAuthorizedKeys parses a public key from an authorized_keys +// file used in OpenSSH according to the sshd(8) manual page. +func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { + for len(in) > 0 { + end := bytes.IndexByte(in, '\n') + if end != -1 { + rest = in[end+1:] + in = in[:end] + } else { + rest = nil + } + + end = bytes.IndexByte(in, '\r') + if end != -1 { + in = in[:end] + } + + in = bytes.TrimSpace(in) + if len(in) == 0 || in[0] == '#' { + in = rest + continue + } + + i := bytes.IndexAny(in, " \t") + if i == -1 { + in = rest + continue + } + + if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { + return out, comment, options, rest, nil + } + + // No key type recognised. Maybe there's an options field at + // the beginning. + var b byte + inQuote := false + var candidateOptions []string + optionStart := 0 + for i, b = range in { + isEnd := !inQuote && (b == ' ' || b == '\t') + if (b == ',' && !inQuote) || isEnd { + if i-optionStart > 0 { + candidateOptions = append(candidateOptions, string(in[optionStart:i])) + } + optionStart = i + 1 + } + if isEnd { + break + } + if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) { + inQuote = !inQuote + } + } + for i < len(in) && (in[i] == ' ' || in[i] == '\t') { + i++ + } + if i == len(in) { + // Invalid line: unmatched quote + in = rest + continue + } + + in = in[i:] + i = bytes.IndexAny(in, " \t") + if i == -1 { + in = rest + continue + } + + if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { + options = candidateOptions + return out, comment, options, rest, nil + } + + in = rest + continue + } + + return nil, "", nil, nil, errors.New("ssh: no key found") +} + +// ParsePublicKey parses an SSH public key formatted for use in +// the SSH wire protocol according to RFC 4253, section 6.6. +func ParsePublicKey(in []byte) (out PublicKey, err error) { + algo, in, ok := parseString(in) + if !ok { + return nil, errShortRead + } + var rest []byte + out, rest, err = parsePubKey(in, string(algo)) + if len(rest) > 0 { + return nil, errors.New("ssh: trailing junk in public key") + } + + return out, err +} + +// MarshalAuthorizedKey serializes key for inclusion in an OpenSSH +// authorized_keys file. The return value ends with newline. +func MarshalAuthorizedKey(key PublicKey) []byte { + b := &bytes.Buffer{} + b.WriteString(key.Type()) + b.WriteByte(' ') + e := base64.NewEncoder(base64.StdEncoding, b) + e.Write(key.Marshal()) + e.Close() + b.WriteByte('\n') + return b.Bytes() +} + +// PublicKey is an abstraction of different types of public keys. +type PublicKey interface { + // Type returns the key's type, e.g. "ssh-rsa". + Type() string + + // Marshal returns the serialized key data in SSH wire format, + // with the name prefix. + Marshal() []byte + + // Verify that sig is a signature on the given data using this + // key. This function will hash the data appropriately first. + Verify(data []byte, sig *Signature) error +} + +// CryptoPublicKey, if implemented by a PublicKey, +// returns the underlying crypto.PublicKey form of the key. +type CryptoPublicKey interface { + CryptoPublicKey() crypto.PublicKey +} + +// A Signer can create signatures that verify against a public key. +type Signer interface { + // PublicKey returns an associated PublicKey instance. + PublicKey() PublicKey + + // Sign returns raw signature for the given data. This method + // will apply the hash specified for the keytype to the data. + Sign(rand io.Reader, data []byte) (*Signature, error) +} + +type rsaPublicKey rsa.PublicKey + +func (r *rsaPublicKey) Type() string { + return "ssh-rsa" +} + +// parseRSA parses an RSA key according to RFC 4253, section 6.6. +func parseRSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + E *big.Int + N *big.Int + Rest []byte `ssh:"rest"` + } + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + if w.E.BitLen() > 24 { + return nil, nil, errors.New("ssh: exponent too large") + } + e := w.E.Int64() + if e < 3 || e&1 == 0 { + return nil, nil, errors.New("ssh: incorrect exponent") + } + + var key rsa.PublicKey + key.E = int(e) + key.N = w.N + return (*rsaPublicKey)(&key), w.Rest, nil +} + +func (r *rsaPublicKey) Marshal() []byte { + e := new(big.Int).SetInt64(int64(r.E)) + // RSA publickey struct layout should match the struct used by + // parseRSACert in the x/crypto/ssh/agent package. + wirekey := struct { + Name string + E *big.Int + N *big.Int + }{ + KeyAlgoRSA, + e, + r.N, + } + return Marshal(&wirekey) +} + +func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != r.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) + } + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob) +} + +func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey { + return (*rsa.PublicKey)(r) +} + +type dsaPublicKey dsa.PublicKey + +func (r *dsaPublicKey) Type() string { + return "ssh-dss" +} + +// parseDSA parses an DSA key according to RFC 4253, section 6.6. +func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + P, Q, G, Y *big.Int + Rest []byte `ssh:"rest"` + } + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + key := &dsaPublicKey{ + Parameters: dsa.Parameters{ + P: w.P, + Q: w.Q, + G: w.G, + }, + Y: w.Y, + } + return key, w.Rest, nil +} + +func (k *dsaPublicKey) Marshal() []byte { + // DSA publickey struct layout should match the struct used by + // parseDSACert in the x/crypto/ssh/agent package. + w := struct { + Name string + P, Q, G, Y *big.Int + }{ + k.Type(), + k.P, + k.Q, + k.G, + k.Y, + } + + return Marshal(&w) +} + +func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != k.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) + } + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + + // Per RFC 4253, section 6.6, + // The value for 'dss_signature_blob' is encoded as a string containing + // r, followed by s (which are 160-bit integers, without lengths or + // padding, unsigned, and in network byte order). + // For DSS purposes, sig.Blob should be exactly 40 bytes in length. + if len(sig.Blob) != 40 { + return errors.New("ssh: DSA signature parse error") + } + r := new(big.Int).SetBytes(sig.Blob[:20]) + s := new(big.Int).SetBytes(sig.Blob[20:]) + if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) { + return nil + } + return errors.New("ssh: signature did not verify") +} + +func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey { + return (*dsa.PublicKey)(k) +} + +type dsaPrivateKey struct { + *dsa.PrivateKey +} + +func (k *dsaPrivateKey) PublicKey() PublicKey { + return (*dsaPublicKey)(&k.PrivateKey.PublicKey) +} + +func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + r, s, err := dsa.Sign(rand, k.PrivateKey, digest) + if err != nil { + return nil, err + } + + sig := make([]byte, 40) + rb := r.Bytes() + sb := s.Bytes() + + copy(sig[20-len(rb):20], rb) + copy(sig[40-len(sb):], sb) + + return &Signature{ + Format: k.PublicKey().Type(), + Blob: sig, + }, nil +} + +type ecdsaPublicKey ecdsa.PublicKey + +func (key *ecdsaPublicKey) Type() string { + return "ecdsa-sha2-" + key.nistID() +} + +func (key *ecdsaPublicKey) nistID() string { + switch key.Params().BitSize { + case 256: + return "nistp256" + case 384: + return "nistp384" + case 521: + return "nistp521" + } + panic("ssh: unsupported ecdsa key size") +} + +type ed25519PublicKey ed25519.PublicKey + +func (key ed25519PublicKey) Type() string { + return KeyAlgoED25519 +} + +func parseED25519(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + KeyBytes []byte + Rest []byte `ssh:"rest"` + } + + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + key := ed25519.PublicKey(w.KeyBytes) + + return (ed25519PublicKey)(key), w.Rest, nil +} + +func (key ed25519PublicKey) Marshal() []byte { + w := struct { + Name string + KeyBytes []byte + }{ + KeyAlgoED25519, + []byte(key), + } + return Marshal(&w) +} + +func (key ed25519PublicKey) Verify(b []byte, sig *Signature) error { + if sig.Format != key.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type()) + } + + edKey := (ed25519.PublicKey)(key) + if ok := ed25519.Verify(edKey, b, sig.Blob); !ok { + return errors.New("ssh: signature did not verify") + } + + return nil +} + +func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey { + return ed25519.PublicKey(k) +} + +func supportedEllipticCurve(curve elliptic.Curve) bool { + return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() +} + +// ecHash returns the hash to match the given elliptic curve, see RFC +// 5656, section 6.2.1 +func ecHash(curve elliptic.Curve) crypto.Hash { + bitSize := curve.Params().BitSize + switch { + case bitSize <= 256: + return crypto.SHA256 + case bitSize <= 384: + return crypto.SHA384 + } + return crypto.SHA512 +} + +// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. +func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + Curve string + KeyBytes []byte + Rest []byte `ssh:"rest"` + } + + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + key := new(ecdsa.PublicKey) + + switch w.Curve { + case "nistp256": + key.Curve = elliptic.P256() + case "nistp384": + key.Curve = elliptic.P384() + case "nistp521": + key.Curve = elliptic.P521() + default: + return nil, nil, errors.New("ssh: unsupported curve") + } + + key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes) + if key.X == nil || key.Y == nil { + return nil, nil, errors.New("ssh: invalid curve point") + } + return (*ecdsaPublicKey)(key), w.Rest, nil +} + +func (key *ecdsaPublicKey) Marshal() []byte { + // See RFC 5656, section 3.1. + keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y) + // ECDSA publickey struct layout should match the struct used by + // parseECDSACert in the x/crypto/ssh/agent package. + w := struct { + Name string + ID string + Key []byte + }{ + key.Type(), + key.nistID(), + keyBytes, + } + + return Marshal(&w) +} + +func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != key.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type()) + } + + h := ecHash(key.Curve).New() + h.Write(data) + digest := h.Sum(nil) + + // Per RFC 5656, section 3.1.2, + // The ecdsa_signature_blob value has the following specific encoding: + // mpint r + // mpint s + var ecSig struct { + R *big.Int + S *big.Int + } + + if err := Unmarshal(sig.Blob, &ecSig); err != nil { + return err + } + + if ecdsa.Verify((*ecdsa.PublicKey)(key), digest, ecSig.R, ecSig.S) { + return nil + } + return errors.New("ssh: signature did not verify") +} + +func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey { + return (*ecdsa.PublicKey)(k) +} + +// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, +// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding +// Signer instance. ECDSA keys must use P-256, P-384 or P-521. +func NewSignerFromKey(key interface{}) (Signer, error) { + switch key := key.(type) { + case crypto.Signer: + return NewSignerFromSigner(key) + case *dsa.PrivateKey: + return &dsaPrivateKey{key}, nil + default: + return nil, fmt.Errorf("ssh: unsupported key type %T", key) + } +} + +type wrappedSigner struct { + signer crypto.Signer + pubKey PublicKey +} + +// NewSignerFromSigner takes any crypto.Signer implementation and +// returns a corresponding Signer interface. This can be used, for +// example, with keys kept in hardware modules. +func NewSignerFromSigner(signer crypto.Signer) (Signer, error) { + pubKey, err := NewPublicKey(signer.Public()) + if err != nil { + return nil, err + } + + return &wrappedSigner{signer, pubKey}, nil +} + +func (s *wrappedSigner) PublicKey() PublicKey { + return s.pubKey +} + +func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { + var hashFunc crypto.Hash + + switch key := s.pubKey.(type) { + case *rsaPublicKey, *dsaPublicKey: + hashFunc = crypto.SHA1 + case *ecdsaPublicKey: + hashFunc = ecHash(key.Curve) + case ed25519PublicKey: + default: + return nil, fmt.Errorf("ssh: unsupported key type %T", key) + } + + var digest []byte + if hashFunc != 0 { + h := hashFunc.New() + h.Write(data) + digest = h.Sum(nil) + } else { + digest = data + } + + signature, err := s.signer.Sign(rand, digest, hashFunc) + if err != nil { + return nil, err + } + + // crypto.Signer.Sign is expected to return an ASN.1-encoded signature + // for ECDSA and DSA, but that's not the encoding expected by SSH, so + // re-encode. + switch s.pubKey.(type) { + case *ecdsaPublicKey, *dsaPublicKey: + type asn1Signature struct { + R, S *big.Int + } + asn1Sig := new(asn1Signature) + _, err := asn1.Unmarshal(signature, asn1Sig) + if err != nil { + return nil, err + } + + switch s.pubKey.(type) { + case *ecdsaPublicKey: + signature = Marshal(asn1Sig) + + case *dsaPublicKey: + signature = make([]byte, 40) + r := asn1Sig.R.Bytes() + s := asn1Sig.S.Bytes() + copy(signature[20-len(r):20], r) + copy(signature[40-len(s):40], s) + } + } + + return &Signature{ + Format: s.pubKey.Type(), + Blob: signature, + }, nil +} + +// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey, +// or ed25519.PublicKey returns a corresponding PublicKey instance. +// ECDSA keys must use P-256, P-384 or P-521. +func NewPublicKey(key interface{}) (PublicKey, error) { + switch key := key.(type) { + case *rsa.PublicKey: + return (*rsaPublicKey)(key), nil + case *ecdsa.PublicKey: + if !supportedEllipticCurve(key.Curve) { + return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported.") + } + return (*ecdsaPublicKey)(key), nil + case *dsa.PublicKey: + return (*dsaPublicKey)(key), nil + case ed25519.PublicKey: + return (ed25519PublicKey)(key), nil + default: + return nil, fmt.Errorf("ssh: unsupported key type %T", key) + } +} + +// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports +// the same keys as ParseRawPrivateKey. +func ParsePrivateKey(pemBytes []byte) (Signer, error) { + key, err := ParseRawPrivateKey(pemBytes) + if err != nil { + return nil, err + } + + return NewSignerFromKey(key) +} + +// encryptedBlock tells whether a private key is +// encrypted by examining its Proc-Type header +// for a mention of ENCRYPTED +// according to RFC 1421 Section 4.6.1.1. +func encryptedBlock(block *pem.Block) bool { + return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED") +} + +// ParseRawPrivateKey returns a private key from a PEM encoded private key. It +// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. +func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("ssh: no key found") + } + + if encryptedBlock(block) { + return nil, errors.New("ssh: cannot decode encrypted private keys") + } + + switch block.Type { + case "RSA PRIVATE KEY": + return x509.ParsePKCS1PrivateKey(block.Bytes) + case "EC PRIVATE KEY": + return x509.ParseECPrivateKey(block.Bytes) + case "DSA PRIVATE KEY": + return ParseDSAPrivateKey(block.Bytes) + case "OPENSSH PRIVATE KEY": + return parseOpenSSHPrivateKey(block.Bytes) + default: + return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) + } +} + +// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as +// specified by the OpenSSL DSA man page. +func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { + var k struct { + Version int + P *big.Int + Q *big.Int + G *big.Int + Pub *big.Int + Priv *big.Int + } + rest, err := asn1.Unmarshal(der, &k) + if err != nil { + return nil, errors.New("ssh: failed to parse DSA key: " + err.Error()) + } + if len(rest) > 0 { + return nil, errors.New("ssh: garbage after DSA key") + } + + return &dsa.PrivateKey{ + PublicKey: dsa.PublicKey{ + Parameters: dsa.Parameters{ + P: k.P, + Q: k.Q, + G: k.G, + }, + Y: k.Pub, + }, + X: k.Priv, + }, nil +} + +// Implemented based on the documentation at +// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key +func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) { + magic := append([]byte("openssh-key-v1"), 0) + if !bytes.Equal(magic, key[0:len(magic)]) { + return nil, errors.New("ssh: invalid openssh private key format") + } + remaining := key[len(magic):] + + var w struct { + CipherName string + KdfName string + KdfOpts string + NumKeys uint32 + PubKey []byte + PrivKeyBlock []byte + } + + if err := Unmarshal(remaining, &w); err != nil { + return nil, err + } + + pk1 := struct { + Check1 uint32 + Check2 uint32 + Keytype string + Pub []byte + Priv []byte + Comment string + Pad []byte `ssh:"rest"` + }{} + + if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil { + return nil, err + } + + if pk1.Check1 != pk1.Check2 { + return nil, errors.New("ssh: checkint mismatch") + } + + // we only handle ed25519 keys currently + if pk1.Keytype != KeyAlgoED25519 { + return nil, errors.New("ssh: unhandled key type") + } + + for i, b := range pk1.Pad { + if int(b) != i+1 { + return nil, errors.New("ssh: padding not as expected") + } + } + + if len(pk1.Priv) != ed25519.PrivateKeySize { + return nil, errors.New("ssh: private key unexpected length") + } + + pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize)) + copy(pk, pk1.Priv) + return &pk, nil +} + +// FingerprintLegacyMD5 returns the user presentation of the key's +// fingerprint as described by RFC 4716 section 4. +func FingerprintLegacyMD5(pubKey PublicKey) string { + md5sum := md5.Sum(pubKey.Marshal()) + hexarray := make([]string, len(md5sum)) + for i, c := range md5sum { + hexarray[i] = hex.EncodeToString([]byte{c}) + } + return strings.Join(hexarray, ":") +} + +// FingerprintSHA256 returns the user presentation of the key's +// fingerprint as unpadded base64 encoded sha256 hash. +// This format was introduced from OpenSSH 6.8. +// https://www.openssh.com/txt/release-6.8 +// https://tools.ietf.org/html/rfc4648#section-3.2 (unpadded base64 encoding) +func FingerprintSHA256(pubKey PublicKey) string { + sha256sum := sha256.Sum256(pubKey.Marshal()) + hash := base64.RawStdEncoding.EncodeToString(sha256sum[:]) + return "SHA256:" + hash +} diff --git a/vendor/golang.org/x/crypto/ssh/mac.go b/vendor/golang.org/x/crypto/ssh/mac.go new file mode 100644 index 000000000..c07a06285 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/mac.go @@ -0,0 +1,61 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +// Message authentication support + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "hash" +) + +type macMode struct { + keySize int + etm bool + new func(key []byte) hash.Hash +} + +// truncatingMAC wraps around a hash.Hash and truncates the output digest to +// a given size. +type truncatingMAC struct { + length int + hmac hash.Hash +} + +func (t truncatingMAC) Write(data []byte) (int, error) { + return t.hmac.Write(data) +} + +func (t truncatingMAC) Sum(in []byte) []byte { + out := t.hmac.Sum(in) + return out[:len(in)+t.length] +} + +func (t truncatingMAC) Reset() { + t.hmac.Reset() +} + +func (t truncatingMAC) Size() int { + return t.length +} + +func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } + +var macModes = map[string]*macMode{ + "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash { + return hmac.New(sha256.New, key) + }}, + "hmac-sha2-256": {32, false, func(key []byte) hash.Hash { + return hmac.New(sha256.New, key) + }}, + "hmac-sha1": {20, false, func(key []byte) hash.Hash { + return hmac.New(sha1.New, key) + }}, + "hmac-sha1-96": {20, false, func(key []byte) hash.Hash { + return truncatingMAC{12, hmac.New(sha1.New, key)} + }}, +} diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go new file mode 100644 index 000000000..e6ecd3afa --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/messages.go @@ -0,0 +1,758 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" + "reflect" + "strconv" + "strings" +) + +// These are SSH message type numbers. They are scattered around several +// documents but many were taken from [SSH-PARAMETERS]. +const ( + msgIgnore = 2 + msgUnimplemented = 3 + msgDebug = 4 + msgNewKeys = 21 + + // Standard authentication messages + msgUserAuthSuccess = 52 + msgUserAuthBanner = 53 +) + +// SSH messages: +// +// These structures mirror the wire format of the corresponding SSH messages. +// They are marshaled using reflection with the marshal and unmarshal functions +// in this file. The only wrinkle is that a final member of type []byte with a +// ssh tag of "rest" receives the remainder of a packet when unmarshaling. + +// See RFC 4253, section 11.1. +const msgDisconnect = 1 + +// disconnectMsg is the message that signals a disconnect. It is also +// the error type returned from mux.Wait() +type disconnectMsg struct { + Reason uint32 `sshtype:"1"` + Message string + Language string +} + +func (d *disconnectMsg) Error() string { + return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message) +} + +// See RFC 4253, section 7.1. +const msgKexInit = 20 + +type kexInitMsg struct { + Cookie [16]byte `sshtype:"20"` + KexAlgos []string + ServerHostKeyAlgos []string + CiphersClientServer []string + CiphersServerClient []string + MACsClientServer []string + MACsServerClient []string + CompressionClientServer []string + CompressionServerClient []string + LanguagesClientServer []string + LanguagesServerClient []string + FirstKexFollows bool + Reserved uint32 +} + +// See RFC 4253, section 8. + +// Diffie-Helman +const msgKexDHInit = 30 + +type kexDHInitMsg struct { + X *big.Int `sshtype:"30"` +} + +const msgKexECDHInit = 30 + +type kexECDHInitMsg struct { + ClientPubKey []byte `sshtype:"30"` +} + +const msgKexECDHReply = 31 + +type kexECDHReplyMsg struct { + HostKey []byte `sshtype:"31"` + EphemeralPubKey []byte + Signature []byte +} + +const msgKexDHReply = 31 + +type kexDHReplyMsg struct { + HostKey []byte `sshtype:"31"` + Y *big.Int + Signature []byte +} + +// See RFC 4253, section 10. +const msgServiceRequest = 5 + +type serviceRequestMsg struct { + Service string `sshtype:"5"` +} + +// See RFC 4253, section 10. +const msgServiceAccept = 6 + +type serviceAcceptMsg struct { + Service string `sshtype:"6"` +} + +// See RFC 4252, section 5. +const msgUserAuthRequest = 50 + +type userAuthRequestMsg struct { + User string `sshtype:"50"` + Service string + Method string + Payload []byte `ssh:"rest"` +} + +// Used for debug printouts of packets. +type userAuthSuccessMsg struct { +} + +// See RFC 4252, section 5.1 +const msgUserAuthFailure = 51 + +type userAuthFailureMsg struct { + Methods []string `sshtype:"51"` + PartialSuccess bool +} + +// See RFC 4256, section 3.2 +const msgUserAuthInfoRequest = 60 +const msgUserAuthInfoResponse = 61 + +type userAuthInfoRequestMsg struct { + User string `sshtype:"60"` + Instruction string + DeprecatedLanguage string + NumPrompts uint32 + Prompts []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +const msgChannelOpen = 90 + +type channelOpenMsg struct { + ChanType string `sshtype:"90"` + PeersId uint32 + PeersWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte `ssh:"rest"` +} + +const msgChannelExtendedData = 95 +const msgChannelData = 94 + +// Used for debug print outs of packets. +type channelDataMsg struct { + PeersId uint32 `sshtype:"94"` + Length uint32 + Rest []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +const msgChannelOpenConfirm = 91 + +type channelOpenConfirmMsg struct { + PeersId uint32 `sshtype:"91"` + MyId uint32 + MyWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +const msgChannelOpenFailure = 92 + +type channelOpenFailureMsg struct { + PeersId uint32 `sshtype:"92"` + Reason RejectionReason + Message string + Language string +} + +const msgChannelRequest = 98 + +type channelRequestMsg struct { + PeersId uint32 `sshtype:"98"` + Request string + WantReply bool + RequestSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.4. +const msgChannelSuccess = 99 + +type channelRequestSuccessMsg struct { + PeersId uint32 `sshtype:"99"` +} + +// See RFC 4254, section 5.4. +const msgChannelFailure = 100 + +type channelRequestFailureMsg struct { + PeersId uint32 `sshtype:"100"` +} + +// See RFC 4254, section 5.3 +const msgChannelClose = 97 + +type channelCloseMsg struct { + PeersId uint32 `sshtype:"97"` +} + +// See RFC 4254, section 5.3 +const msgChannelEOF = 96 + +type channelEOFMsg struct { + PeersId uint32 `sshtype:"96"` +} + +// See RFC 4254, section 4 +const msgGlobalRequest = 80 + +type globalRequestMsg struct { + Type string `sshtype:"80"` + WantReply bool + Data []byte `ssh:"rest"` +} + +// See RFC 4254, section 4 +const msgRequestSuccess = 81 + +type globalRequestSuccessMsg struct { + Data []byte `ssh:"rest" sshtype:"81"` +} + +// See RFC 4254, section 4 +const msgRequestFailure = 82 + +type globalRequestFailureMsg struct { + Data []byte `ssh:"rest" sshtype:"82"` +} + +// See RFC 4254, section 5.2 +const msgChannelWindowAdjust = 93 + +type windowAdjustMsg struct { + PeersId uint32 `sshtype:"93"` + AdditionalBytes uint32 +} + +// See RFC 4252, section 7 +const msgUserAuthPubKeyOk = 60 + +type userAuthPubKeyOkMsg struct { + Algo string `sshtype:"60"` + PubKey []byte +} + +// typeTags returns the possible type bytes for the given reflect.Type, which +// should be a struct. The possible values are separated by a '|' character. +func typeTags(structType reflect.Type) (tags []byte) { + tagStr := structType.Field(0).Tag.Get("sshtype") + + for _, tag := range strings.Split(tagStr, "|") { + i, err := strconv.Atoi(tag) + if err == nil { + tags = append(tags, byte(i)) + } + } + + return tags +} + +func fieldError(t reflect.Type, field int, problem string) error { + if problem != "" { + problem = ": " + problem + } + return fmt.Errorf("ssh: unmarshal error for field %s of type %s%s", t.Field(field).Name, t.Name(), problem) +} + +var errShortRead = errors.New("ssh: short read") + +// Unmarshal parses data in SSH wire format into a structure. The out +// argument should be a pointer to struct. If the first member of the +// struct has the "sshtype" tag set to a '|'-separated set of numbers +// in decimal, the packet must start with one of those numbers. In +// case of error, Unmarshal returns a ParseError or +// UnexpectedMessageError. +func Unmarshal(data []byte, out interface{}) error { + v := reflect.ValueOf(out).Elem() + structType := v.Type() + expectedTypes := typeTags(structType) + + var expectedType byte + if len(expectedTypes) > 0 { + expectedType = expectedTypes[0] + } + + if len(data) == 0 { + return parseError(expectedType) + } + + if len(expectedTypes) > 0 { + goodType := false + for _, e := range expectedTypes { + if e > 0 && data[0] == e { + goodType = true + break + } + } + if !goodType { + return fmt.Errorf("ssh: unexpected message type %d (expected one of %v)", data[0], expectedTypes) + } + data = data[1:] + } + + var ok bool + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + t := field.Type() + switch t.Kind() { + case reflect.Bool: + if len(data) < 1 { + return errShortRead + } + field.SetBool(data[0] != 0) + data = data[1:] + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + return fieldError(structType, i, "array of unsupported type") + } + if len(data) < t.Len() { + return errShortRead + } + for j, n := 0, t.Len(); j < n; j++ { + field.Index(j).Set(reflect.ValueOf(data[j])) + } + data = data[t.Len():] + case reflect.Uint64: + var u64 uint64 + if u64, data, ok = parseUint64(data); !ok { + return errShortRead + } + field.SetUint(u64) + case reflect.Uint32: + var u32 uint32 + if u32, data, ok = parseUint32(data); !ok { + return errShortRead + } + field.SetUint(uint64(u32)) + case reflect.Uint8: + if len(data) < 1 { + return errShortRead + } + field.SetUint(uint64(data[0])) + data = data[1:] + case reflect.String: + var s []byte + if s, data, ok = parseString(data); !ok { + return fieldError(structType, i, "") + } + field.SetString(string(s)) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + if structType.Field(i).Tag.Get("ssh") == "rest" { + field.Set(reflect.ValueOf(data)) + data = nil + } else { + var s []byte + if s, data, ok = parseString(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(s)) + } + case reflect.String: + var nl []string + if nl, data, ok = parseNameList(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(nl)) + default: + return fieldError(structType, i, "slice of unsupported type") + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + if n, data, ok = parseInt(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(n)) + } else { + return fieldError(structType, i, "pointer to unsupported type") + } + default: + return fieldError(structType, i, fmt.Sprintf("unsupported type: %v", t)) + } + } + + if len(data) != 0 { + return parseError(expectedType) + } + + return nil +} + +// Marshal serializes the message in msg to SSH wire format. The msg +// argument should be a struct or pointer to struct. If the first +// member has the "sshtype" tag set to a number in decimal, that +// number is prepended to the result. If the last of member has the +// "ssh" tag set to "rest", its contents are appended to the output. +func Marshal(msg interface{}) []byte { + out := make([]byte, 0, 64) + return marshalStruct(out, msg) +} + +func marshalStruct(out []byte, msg interface{}) []byte { + v := reflect.Indirect(reflect.ValueOf(msg)) + msgTypes := typeTags(v.Type()) + if len(msgTypes) > 0 { + out = append(out, msgTypes[0]) + } + + for i, n := 0, v.NumField(); i < n; i++ { + field := v.Field(i) + switch t := field.Type(); t.Kind() { + case reflect.Bool: + var v uint8 + if field.Bool() { + v = 1 + } + out = append(out, v) + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + panic(fmt.Sprintf("array of non-uint8 in field %d: %T", i, field.Interface())) + } + for j, l := 0, t.Len(); j < l; j++ { + out = append(out, uint8(field.Index(j).Uint())) + } + case reflect.Uint32: + out = appendU32(out, uint32(field.Uint())) + case reflect.Uint64: + out = appendU64(out, uint64(field.Uint())) + case reflect.Uint8: + out = append(out, uint8(field.Uint())) + case reflect.String: + s := field.String() + out = appendInt(out, len(s)) + out = append(out, s...) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + if v.Type().Field(i).Tag.Get("ssh") != "rest" { + out = appendInt(out, field.Len()) + } + out = append(out, field.Bytes()...) + case reflect.String: + offset := len(out) + out = appendU32(out, 0) + if n := field.Len(); n > 0 { + for j := 0; j < n; j++ { + f := field.Index(j) + if j != 0 { + out = append(out, ',') + } + out = append(out, f.String()...) + } + // overwrite length value + binary.BigEndian.PutUint32(out[offset:], uint32(len(out)-offset-4)) + } + default: + panic(fmt.Sprintf("slice of unknown type in field %d: %T", i, field.Interface())) + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + nValue := reflect.ValueOf(&n) + nValue.Elem().Set(field) + needed := intLength(n) + oldLength := len(out) + + if cap(out)-len(out) < needed { + newOut := make([]byte, len(out), 2*(len(out)+needed)) + copy(newOut, out) + out = newOut + } + out = out[:oldLength+needed] + marshalInt(out[oldLength:], n) + } else { + panic(fmt.Sprintf("pointer to unknown type in field %d: %T", i, field.Interface())) + } + } + } + + return out +} + +var bigOne = big.NewInt(1) + +func parseString(in []byte) (out, rest []byte, ok bool) { + if len(in) < 4 { + return + } + length := binary.BigEndian.Uint32(in) + in = in[4:] + if uint32(len(in)) < length { + return + } + out = in[:length] + rest = in[length:] + ok = true + return +} + +var ( + comma = []byte{','} + emptyNameList = []string{} +) + +func parseNameList(in []byte) (out []string, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + if len(contents) == 0 { + out = emptyNameList + return + } + parts := bytes.Split(contents, comma) + out = make([]string, len(parts)) + for i, part := range parts { + out[i] = string(part) + } + return +} + +func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + out = new(big.Int) + + if len(contents) > 0 && contents[0]&0x80 == 0x80 { + // This is a negative number + notBytes := make([]byte, len(contents)) + for i := range notBytes { + notBytes[i] = ^contents[i] + } + out.SetBytes(notBytes) + out.Add(out, bigOne) + out.Neg(out) + } else { + // Positive number + out.SetBytes(contents) + } + ok = true + return +} + +func parseUint32(in []byte) (uint32, []byte, bool) { + if len(in) < 4 { + return 0, nil, false + } + return binary.BigEndian.Uint32(in), in[4:], true +} + +func parseUint64(in []byte) (uint64, []byte, bool) { + if len(in) < 8 { + return 0, nil, false + } + return binary.BigEndian.Uint64(in), in[8:], true +} + +func intLength(n *big.Int) int { + length := 4 /* length bytes */ + if n.Sign() < 0 { + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bitLen := nMinus1.BitLen() + if bitLen%8 == 0 { + // The number will need 0xff padding + length++ + } + length += (bitLen + 7) / 8 + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bitLen := n.BitLen() + if bitLen%8 == 0 { + // The number will need 0x00 padding + length++ + } + length += (bitLen + 7) / 8 + } + + return length +} + +func marshalUint32(to []byte, n uint32) []byte { + binary.BigEndian.PutUint32(to, n) + return to[4:] +} + +func marshalUint64(to []byte, n uint64) []byte { + binary.BigEndian.PutUint64(to, n) + return to[8:] +} + +func marshalInt(to []byte, n *big.Int) []byte { + lengthBytes := to + to = to[4:] + length := 0 + + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement + // form. So we'll subtract 1 and invert. If the + // most-significant-bit isn't set then we'll need to pad the + // beginning with 0xff in order to keep the number negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + to[0] = 0xff + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bytes := n.Bytes() + if len(bytes) > 0 && bytes[0]&0x80 != 0 { + // We'll have to pad this with a 0x00 in order to + // stop it looking like a negative number. + to[0] = 0 + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } + + lengthBytes[0] = byte(length >> 24) + lengthBytes[1] = byte(length >> 16) + lengthBytes[2] = byte(length >> 8) + lengthBytes[3] = byte(length) + return to +} + +func writeInt(w io.Writer, n *big.Int) { + length := intLength(n) + buf := make([]byte, length) + marshalInt(buf, n) + w.Write(buf) +} + +func writeString(w io.Writer, s []byte) { + var lengthBytes [4]byte + lengthBytes[0] = byte(len(s) >> 24) + lengthBytes[1] = byte(len(s) >> 16) + lengthBytes[2] = byte(len(s) >> 8) + lengthBytes[3] = byte(len(s)) + w.Write(lengthBytes[:]) + w.Write(s) +} + +func stringLength(n int) int { + return 4 + n +} + +func marshalString(to []byte, s []byte) []byte { + to[0] = byte(len(s) >> 24) + to[1] = byte(len(s) >> 16) + to[2] = byte(len(s) >> 8) + to[3] = byte(len(s)) + to = to[4:] + copy(to, s) + return to[len(s):] +} + +var bigIntType = reflect.TypeOf((*big.Int)(nil)) + +// Decode a packet into its corresponding message. +func decode(packet []byte) (interface{}, error) { + var msg interface{} + switch packet[0] { + case msgDisconnect: + msg = new(disconnectMsg) + case msgServiceRequest: + msg = new(serviceRequestMsg) + case msgServiceAccept: + msg = new(serviceAcceptMsg) + case msgKexInit: + msg = new(kexInitMsg) + case msgKexDHInit: + msg = new(kexDHInitMsg) + case msgKexDHReply: + msg = new(kexDHReplyMsg) + case msgUserAuthRequest: + msg = new(userAuthRequestMsg) + case msgUserAuthSuccess: + return new(userAuthSuccessMsg), nil + case msgUserAuthFailure: + msg = new(userAuthFailureMsg) + case msgUserAuthPubKeyOk: + msg = new(userAuthPubKeyOkMsg) + case msgGlobalRequest: + msg = new(globalRequestMsg) + case msgRequestSuccess: + msg = new(globalRequestSuccessMsg) + case msgRequestFailure: + msg = new(globalRequestFailureMsg) + case msgChannelOpen: + msg = new(channelOpenMsg) + case msgChannelData: + msg = new(channelDataMsg) + case msgChannelOpenConfirm: + msg = new(channelOpenConfirmMsg) + case msgChannelOpenFailure: + msg = new(channelOpenFailureMsg) + case msgChannelWindowAdjust: + msg = new(windowAdjustMsg) + case msgChannelEOF: + msg = new(channelEOFMsg) + case msgChannelClose: + msg = new(channelCloseMsg) + case msgChannelRequest: + msg = new(channelRequestMsg) + case msgChannelSuccess: + msg = new(channelRequestSuccessMsg) + case msgChannelFailure: + msg = new(channelRequestFailureMsg) + default: + return nil, unexpectedMessageError(0, packet[0]) + } + if err := Unmarshal(packet, msg); err != nil { + return nil, err + } + return msg, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/mux.go b/vendor/golang.org/x/crypto/ssh/mux.go new file mode 100644 index 000000000..27a527c10 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/mux.go @@ -0,0 +1,330 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "encoding/binary" + "fmt" + "io" + "log" + "sync" + "sync/atomic" +) + +// debugMux, if set, causes messages in the connection protocol to be +// logged. +const debugMux = false + +// chanList is a thread safe channel list. +type chanList struct { + // protects concurrent access to chans + sync.Mutex + + // chans are indexed by the local id of the channel, which the + // other side should send in the PeersId field. + chans []*channel + + // This is a debugging aid: it offsets all IDs by this + // amount. This helps distinguish otherwise identical + // server/client muxes + offset uint32 +} + +// Assigns a channel ID to the given channel. +func (c *chanList) add(ch *channel) uint32 { + c.Lock() + defer c.Unlock() + for i := range c.chans { + if c.chans[i] == nil { + c.chans[i] = ch + return uint32(i) + c.offset + } + } + c.chans = append(c.chans, ch) + return uint32(len(c.chans)-1) + c.offset +} + +// getChan returns the channel for the given ID. +func (c *chanList) getChan(id uint32) *channel { + id -= c.offset + + c.Lock() + defer c.Unlock() + if id < uint32(len(c.chans)) { + return c.chans[id] + } + return nil +} + +func (c *chanList) remove(id uint32) { + id -= c.offset + c.Lock() + if id < uint32(len(c.chans)) { + c.chans[id] = nil + } + c.Unlock() +} + +// dropAll forgets all channels it knows, returning them in a slice. +func (c *chanList) dropAll() []*channel { + c.Lock() + defer c.Unlock() + var r []*channel + + for _, ch := range c.chans { + if ch == nil { + continue + } + r = append(r, ch) + } + c.chans = nil + return r +} + +// mux represents the state for the SSH connection protocol, which +// multiplexes many channels onto a single packet transport. +type mux struct { + conn packetConn + chanList chanList + + incomingChannels chan NewChannel + + globalSentMu sync.Mutex + globalResponses chan interface{} + incomingRequests chan *Request + + errCond *sync.Cond + err error +} + +// When debugging, each new chanList instantiation has a different +// offset. +var globalOff uint32 + +func (m *mux) Wait() error { + m.errCond.L.Lock() + defer m.errCond.L.Unlock() + for m.err == nil { + m.errCond.Wait() + } + return m.err +} + +// newMux returns a mux that runs over the given connection. +func newMux(p packetConn) *mux { + m := &mux{ + conn: p, + incomingChannels: make(chan NewChannel, chanSize), + globalResponses: make(chan interface{}, 1), + incomingRequests: make(chan *Request, chanSize), + errCond: newCond(), + } + if debugMux { + m.chanList.offset = atomic.AddUint32(&globalOff, 1) + } + + go m.loop() + return m +} + +func (m *mux) sendMessage(msg interface{}) error { + p := Marshal(msg) + if debugMux { + log.Printf("send global(%d): %#v", m.chanList.offset, msg) + } + return m.conn.writePacket(p) +} + +func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) { + if wantReply { + m.globalSentMu.Lock() + defer m.globalSentMu.Unlock() + } + + if err := m.sendMessage(globalRequestMsg{ + Type: name, + WantReply: wantReply, + Data: payload, + }); err != nil { + return false, nil, err + } + + if !wantReply { + return false, nil, nil + } + + msg, ok := <-m.globalResponses + if !ok { + return false, nil, io.EOF + } + switch msg := msg.(type) { + case *globalRequestFailureMsg: + return false, msg.Data, nil + case *globalRequestSuccessMsg: + return true, msg.Data, nil + default: + return false, nil, fmt.Errorf("ssh: unexpected response to request: %#v", msg) + } +} + +// ackRequest must be called after processing a global request that +// has WantReply set. +func (m *mux) ackRequest(ok bool, data []byte) error { + if ok { + return m.sendMessage(globalRequestSuccessMsg{Data: data}) + } + return m.sendMessage(globalRequestFailureMsg{Data: data}) +} + +func (m *mux) Close() error { + return m.conn.Close() +} + +// loop runs the connection machine. It will process packets until an +// error is encountered. To synchronize on loop exit, use mux.Wait. +func (m *mux) loop() { + var err error + for err == nil { + err = m.onePacket() + } + + for _, ch := range m.chanList.dropAll() { + ch.close() + } + + close(m.incomingChannels) + close(m.incomingRequests) + close(m.globalResponses) + + m.conn.Close() + + m.errCond.L.Lock() + m.err = err + m.errCond.Broadcast() + m.errCond.L.Unlock() + + if debugMux { + log.Println("loop exit", err) + } +} + +// onePacket reads and processes one packet. +func (m *mux) onePacket() error { + packet, err := m.conn.readPacket() + if err != nil { + return err + } + + if debugMux { + if packet[0] == msgChannelData || packet[0] == msgChannelExtendedData { + log.Printf("decoding(%d): data packet - %d bytes", m.chanList.offset, len(packet)) + } else { + p, _ := decode(packet) + log.Printf("decoding(%d): %d %#v - %d bytes", m.chanList.offset, packet[0], p, len(packet)) + } + } + + switch packet[0] { + case msgChannelOpen: + return m.handleChannelOpen(packet) + case msgGlobalRequest, msgRequestSuccess, msgRequestFailure: + return m.handleGlobalPacket(packet) + } + + // assume a channel packet. + if len(packet) < 5 { + return parseError(packet[0]) + } + id := binary.BigEndian.Uint32(packet[1:]) + ch := m.chanList.getChan(id) + if ch == nil { + return fmt.Errorf("ssh: invalid channel %d", id) + } + + return ch.handlePacket(packet) +} + +func (m *mux) handleGlobalPacket(packet []byte) error { + msg, err := decode(packet) + if err != nil { + return err + } + + switch msg := msg.(type) { + case *globalRequestMsg: + m.incomingRequests <- &Request{ + Type: msg.Type, + WantReply: msg.WantReply, + Payload: msg.Data, + mux: m, + } + case *globalRequestSuccessMsg, *globalRequestFailureMsg: + m.globalResponses <- msg + default: + panic(fmt.Sprintf("not a global message %#v", msg)) + } + + return nil +} + +// handleChannelOpen schedules a channel to be Accept()ed. +func (m *mux) handleChannelOpen(packet []byte) error { + var msg channelOpenMsg + if err := Unmarshal(packet, &msg); err != nil { + return err + } + + if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { + failMsg := channelOpenFailureMsg{ + PeersId: msg.PeersId, + Reason: ConnectionFailed, + Message: "invalid request", + Language: "en_US.UTF-8", + } + return m.sendMessage(failMsg) + } + + c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData) + c.remoteId = msg.PeersId + c.maxRemotePayload = msg.MaxPacketSize + c.remoteWin.add(msg.PeersWindow) + m.incomingChannels <- c + return nil +} + +func (m *mux) OpenChannel(chanType string, extra []byte) (Channel, <-chan *Request, error) { + ch, err := m.openChannel(chanType, extra) + if err != nil { + return nil, nil, err + } + + return ch, ch.incomingRequests, nil +} + +func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) { + ch := m.newChannel(chanType, channelOutbound, extra) + + ch.maxIncomingPayload = channelMaxPacket + + open := channelOpenMsg{ + ChanType: chanType, + PeersWindow: ch.myWindow, + MaxPacketSize: ch.maxIncomingPayload, + TypeSpecificData: extra, + PeersId: ch.localId, + } + if err := m.sendMessage(open); err != nil { + return nil, err + } + + switch msg := (<-ch.msg).(type) { + case *channelOpenConfirmMsg: + return ch, nil + case *channelOpenFailureMsg: + return nil, &OpenChannelError{msg.Reason, msg.Message} + default: + return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go new file mode 100644 index 000000000..77c84d165 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -0,0 +1,491 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" + "strings" +) + +// The Permissions type holds fine-grained permissions that are +// specific to a user or a specific authentication method for a +// user. Permissions, except for "source-address", must be enforced in +// the server application layer, after successful authentication. The +// Permissions are passed on in ServerConn so a server implementation +// can honor them. +type Permissions struct { + // Critical options restrict default permissions. Common + // restrictions are "source-address" and "force-command". If + // the server cannot enforce the restriction, or does not + // recognize it, the user should not authenticate. + CriticalOptions map[string]string + + // Extensions are extra functionality that the server may + // offer on authenticated connections. Common extensions are + // "permit-agent-forwarding", "permit-X11-forwarding". Lack of + // support for an extension does not preclude authenticating a + // user. + Extensions map[string]string +} + +// ServerConfig holds server specific configuration data. +type ServerConfig struct { + // Config contains configuration shared between client and server. + Config + + hostKeys []Signer + + // NoClientAuth is true if clients are allowed to connect without + // authenticating. + NoClientAuth bool + + // PasswordCallback, if non-nil, is called when a user + // attempts to authenticate using a password. + PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) + + // PublicKeyCallback, if non-nil, is called when a client attempts public + // key authentication. It must return true if the given public key is + // valid for the given user. For example, see CertChecker.Authenticate. + PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) + + // KeyboardInteractiveCallback, if non-nil, is called when + // keyboard-interactive authentication is selected (RFC + // 4256). The client object's Challenge function should be + // used to query the user. The callback may offer multiple + // Challenge rounds. To avoid information leaks, the client + // should be presented a challenge even if the user is + // unknown. + KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error) + + // AuthLogCallback, if non-nil, is called to log all authentication + // attempts. + AuthLogCallback func(conn ConnMetadata, method string, err error) + + // ServerVersion is the version identification string to announce in + // the public handshake. + // If empty, a reasonable default is used. + // Note that RFC 4253 section 4.2 requires that this string start with + // "SSH-2.0-". + ServerVersion string +} + +// AddHostKey adds a private key as a host key. If an existing host +// key exists with the same algorithm, it is overwritten. Each server +// config must have at least one host key. +func (s *ServerConfig) AddHostKey(key Signer) { + for i, k := range s.hostKeys { + if k.PublicKey().Type() == key.PublicKey().Type() { + s.hostKeys[i] = key + return + } + } + + s.hostKeys = append(s.hostKeys, key) +} + +// cachedPubKey contains the results of querying whether a public key is +// acceptable for a user. +type cachedPubKey struct { + user string + pubKeyData []byte + result error + perms *Permissions +} + +const maxCachedPubKeys = 16 + +// pubKeyCache caches tests for public keys. Since SSH clients +// will query whether a public key is acceptable before attempting to +// authenticate with it, we end up with duplicate queries for public +// key validity. The cache only applies to a single ServerConn. +type pubKeyCache struct { + keys []cachedPubKey +} + +// get returns the result for a given user/algo/key tuple. +func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) { + for _, k := range c.keys { + if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) { + return k, true + } + } + return cachedPubKey{}, false +} + +// add adds the given tuple to the cache. +func (c *pubKeyCache) add(candidate cachedPubKey) { + if len(c.keys) < maxCachedPubKeys { + c.keys = append(c.keys, candidate) + } +} + +// ServerConn is an authenticated SSH connection, as seen from the +// server +type ServerConn struct { + Conn + + // If the succeeding authentication callback returned a + // non-nil Permissions pointer, it is stored here. + Permissions *Permissions +} + +// NewServerConn starts a new SSH server with c as the underlying +// transport. It starts with a handshake and, if the handshake is +// unsuccessful, it closes the connection and returns an error. The +// Request and NewChannel channels must be serviced, or the connection +// will hang. +func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) { + fullConf := *config + fullConf.SetDefaults() + s := &connection{ + sshConn: sshConn{conn: c}, + } + perms, err := s.serverHandshake(&fullConf) + if err != nil { + c.Close() + return nil, nil, nil, err + } + return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil +} + +// signAndMarshal signs the data with the appropriate algorithm, +// and serializes the result in SSH wire format. +func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { + sig, err := k.Sign(rand, data) + if err != nil { + return nil, err + } + + return Marshal(sig), nil +} + +// handshake performs key exchange and user authentication. +func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) { + if len(config.hostKeys) == 0 { + return nil, errors.New("ssh: server has no host keys") + } + + if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil { + return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") + } + + if config.ServerVersion != "" { + s.serverVersion = []byte(config.ServerVersion) + } else { + s.serverVersion = []byte(packageVersion) + } + var err error + s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion) + if err != nil { + return nil, err + } + + tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) + s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) + + if err := s.transport.waitSession(); err != nil { + return nil, err + } + + // We just did the key change, so the session ID is established. + s.sessionID = s.transport.getSessionID() + + var packet []byte + if packet, err = s.transport.readPacket(); err != nil { + return nil, err + } + + var serviceRequest serviceRequestMsg + if err = Unmarshal(packet, &serviceRequest); err != nil { + return nil, err + } + if serviceRequest.Service != serviceUserAuth { + return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating") + } + serviceAccept := serviceAcceptMsg{ + Service: serviceUserAuth, + } + if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil { + return nil, err + } + + perms, err := s.serverAuthenticate(config) + if err != nil { + return nil, err + } + s.mux = newMux(s.transport) + return perms, err +} + +func isAcceptableAlgo(algo string) bool { + switch algo { + case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519, + CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: + return true + } + return false +} + +func checkSourceAddress(addr net.Addr, sourceAddrs string) error { + if addr == nil { + return errors.New("ssh: no address known for client, but source-address match required") + } + + tcpAddr, ok := addr.(*net.TCPAddr) + if !ok { + return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr) + } + + for _, sourceAddr := range strings.Split(sourceAddrs, ",") { + if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { + if allowedIP.Equal(tcpAddr.IP) { + return nil + } + } else { + _, ipNet, err := net.ParseCIDR(sourceAddr) + if err != nil { + return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err) + } + + if ipNet.Contains(tcpAddr.IP) { + return nil + } + } + } + + return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) +} + +func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { + sessionID := s.transport.getSessionID() + var cache pubKeyCache + var perms *Permissions + +userAuthLoop: + for { + var userAuthReq userAuthRequestMsg + if packet, err := s.transport.readPacket(); err != nil { + return nil, err + } else if err = Unmarshal(packet, &userAuthReq); err != nil { + return nil, err + } + + if userAuthReq.Service != serviceSSH { + return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) + } + + s.user = userAuthReq.User + perms = nil + authErr := errors.New("no auth passed yet") + + switch userAuthReq.Method { + case "none": + if config.NoClientAuth { + authErr = nil + } + case "password": + if config.PasswordCallback == nil { + authErr = errors.New("ssh: password auth not configured") + break + } + payload := userAuthReq.Payload + if len(payload) < 1 || payload[0] != 0 { + return nil, parseError(msgUserAuthRequest) + } + payload = payload[1:] + password, payload, ok := parseString(payload) + if !ok || len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + + perms, authErr = config.PasswordCallback(s, password) + case "keyboard-interactive": + if config.KeyboardInteractiveCallback == nil { + authErr = errors.New("ssh: keyboard-interactive auth not configubred") + break + } + + prompter := &sshClientKeyboardInteractive{s} + perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge) + case "publickey": + if config.PublicKeyCallback == nil { + authErr = errors.New("ssh: publickey auth not configured") + break + } + payload := userAuthReq.Payload + if len(payload) < 1 { + return nil, parseError(msgUserAuthRequest) + } + isQuery := payload[0] == 0 + payload = payload[1:] + algoBytes, payload, ok := parseString(payload) + if !ok { + return nil, parseError(msgUserAuthRequest) + } + algo := string(algoBytes) + if !isAcceptableAlgo(algo) { + authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo) + break + } + + pubKeyData, payload, ok := parseString(payload) + if !ok { + return nil, parseError(msgUserAuthRequest) + } + + pubKey, err := ParsePublicKey(pubKeyData) + if err != nil { + return nil, err + } + + candidate, ok := cache.get(s.user, pubKeyData) + if !ok { + candidate.user = s.user + candidate.pubKeyData = pubKeyData + candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey) + if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" { + candidate.result = checkSourceAddress( + s.RemoteAddr(), + candidate.perms.CriticalOptions[sourceAddressCriticalOption]) + } + cache.add(candidate) + } + + if isQuery { + // The client can query if the given public key + // would be okay. + if len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + + if candidate.result == nil { + okMsg := userAuthPubKeyOkMsg{ + Algo: algo, + PubKey: pubKeyData, + } + if err = s.transport.writePacket(Marshal(&okMsg)); err != nil { + return nil, err + } + continue userAuthLoop + } + authErr = candidate.result + } else { + sig, payload, ok := parseSignature(payload) + if !ok || len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + // Ensure the public key algo and signature algo + // are supported. Compare the private key + // algorithm name that corresponds to algo with + // sig.Format. This is usually the same, but + // for certs, the names differ. + if !isAcceptableAlgo(sig.Format) { + break + } + signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData) + + if err := pubKey.Verify(signedData, sig); err != nil { + return nil, err + } + + authErr = candidate.result + perms = candidate.perms + } + default: + authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method) + } + + if config.AuthLogCallback != nil { + config.AuthLogCallback(s, userAuthReq.Method, authErr) + } + + if authErr == nil { + break userAuthLoop + } + + var failureMsg userAuthFailureMsg + if config.PasswordCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "password") + } + if config.PublicKeyCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "publickey") + } + if config.KeyboardInteractiveCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive") + } + + if len(failureMsg.Methods) == 0 { + return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") + } + + if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil { + return nil, err + } + } + + if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil { + return nil, err + } + return perms, nil +} + +// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by +// asking the client on the other side of a ServerConn. +type sshClientKeyboardInteractive struct { + *connection +} + +func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) { + if len(questions) != len(echos) { + return nil, errors.New("ssh: echos and questions must have equal length") + } + + var prompts []byte + for i := range questions { + prompts = appendString(prompts, questions[i]) + prompts = appendBool(prompts, echos[i]) + } + + if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{ + Instruction: instruction, + NumPrompts: uint32(len(questions)), + Prompts: prompts, + })); err != nil { + return nil, err + } + + packet, err := c.transport.readPacket() + if err != nil { + return nil, err + } + if packet[0] != msgUserAuthInfoResponse { + return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0]) + } + packet = packet[1:] + + n, packet, ok := parseUint32(packet) + if !ok || int(n) != len(questions) { + return nil, parseError(msgUserAuthInfoResponse) + } + + for i := uint32(0); i < n; i++ { + ans, rest, ok := parseString(packet) + if !ok { + return nil, parseError(msgUserAuthInfoResponse) + } + + answers = append(answers, string(ans)) + packet = rest + } + if len(packet) != 0 { + return nil, errors.New("ssh: junk at end of message") + } + + return answers, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go new file mode 100644 index 000000000..17e2aa85c --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/session.go @@ -0,0 +1,627 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +// Session implements an interactive session described in +// "RFC 4254, section 6". + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "sync" +) + +type Signal string + +// POSIX signals as listed in RFC 4254 Section 6.10. +const ( + SIGABRT Signal = "ABRT" + SIGALRM Signal = "ALRM" + SIGFPE Signal = "FPE" + SIGHUP Signal = "HUP" + SIGILL Signal = "ILL" + SIGINT Signal = "INT" + SIGKILL Signal = "KILL" + SIGPIPE Signal = "PIPE" + SIGQUIT Signal = "QUIT" + SIGSEGV Signal = "SEGV" + SIGTERM Signal = "TERM" + SIGUSR1 Signal = "USR1" + SIGUSR2 Signal = "USR2" +) + +var signals = map[Signal]int{ + SIGABRT: 6, + SIGALRM: 14, + SIGFPE: 8, + SIGHUP: 1, + SIGILL: 4, + SIGINT: 2, + SIGKILL: 9, + SIGPIPE: 13, + SIGQUIT: 3, + SIGSEGV: 11, + SIGTERM: 15, +} + +type TerminalModes map[uint8]uint32 + +// POSIX terminal mode flags as listed in RFC 4254 Section 8. +const ( + tty_OP_END = 0 + VINTR = 1 + VQUIT = 2 + VERASE = 3 + VKILL = 4 + VEOF = 5 + VEOL = 6 + VEOL2 = 7 + VSTART = 8 + VSTOP = 9 + VSUSP = 10 + VDSUSP = 11 + VREPRINT = 12 + VWERASE = 13 + VLNEXT = 14 + VFLUSH = 15 + VSWTCH = 16 + VSTATUS = 17 + VDISCARD = 18 + IGNPAR = 30 + PARMRK = 31 + INPCK = 32 + ISTRIP = 33 + INLCR = 34 + IGNCR = 35 + ICRNL = 36 + IUCLC = 37 + IXON = 38 + IXANY = 39 + IXOFF = 40 + IMAXBEL = 41 + ISIG = 50 + ICANON = 51 + XCASE = 52 + ECHO = 53 + ECHOE = 54 + ECHOK = 55 + ECHONL = 56 + NOFLSH = 57 + TOSTOP = 58 + IEXTEN = 59 + ECHOCTL = 60 + ECHOKE = 61 + PENDIN = 62 + OPOST = 70 + OLCUC = 71 + ONLCR = 72 + OCRNL = 73 + ONOCR = 74 + ONLRET = 75 + CS7 = 90 + CS8 = 91 + PARENB = 92 + PARODD = 93 + TTY_OP_ISPEED = 128 + TTY_OP_OSPEED = 129 +) + +// A Session represents a connection to a remote command or shell. +type Session struct { + // Stdin specifies the remote process's standard input. + // If Stdin is nil, the remote process reads from an empty + // bytes.Buffer. + Stdin io.Reader + + // Stdout and Stderr specify the remote process's standard + // output and error. + // + // If either is nil, Run connects the corresponding file + // descriptor to an instance of ioutil.Discard. There is a + // fixed amount of buffering that is shared for the two streams. + // If either blocks it may eventually cause the remote + // command to block. + Stdout io.Writer + Stderr io.Writer + + ch Channel // the channel backing this session + started bool // true once Start, Run or Shell is invoked. + copyFuncs []func() error + errors chan error // one send per copyFunc + + // true if pipe method is active + stdinpipe, stdoutpipe, stderrpipe bool + + // stdinPipeWriter is non-nil if StdinPipe has not been called + // and Stdin was specified by the user; it is the write end of + // a pipe connecting Session.Stdin to the stdin channel. + stdinPipeWriter io.WriteCloser + + exitStatus chan error +} + +// SendRequest sends an out-of-band channel request on the SSH channel +// underlying the session. +func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { + return s.ch.SendRequest(name, wantReply, payload) +} + +func (s *Session) Close() error { + return s.ch.Close() +} + +// RFC 4254 Section 6.4. +type setenvRequest struct { + Name string + Value string +} + +// Setenv sets an environment variable that will be applied to any +// command executed by Shell or Run. +func (s *Session) Setenv(name, value string) error { + msg := setenvRequest{ + Name: name, + Value: value, + } + ok, err := s.ch.SendRequest("env", true, Marshal(&msg)) + if err == nil && !ok { + err = errors.New("ssh: setenv failed") + } + return err +} + +// RFC 4254 Section 6.2. +type ptyRequestMsg struct { + Term string + Columns uint32 + Rows uint32 + Width uint32 + Height uint32 + Modelist string +} + +// RequestPty requests the association of a pty with the session on the remote host. +func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error { + var tm []byte + for k, v := range termmodes { + kv := struct { + Key byte + Val uint32 + }{k, v} + + tm = append(tm, Marshal(&kv)...) + } + tm = append(tm, tty_OP_END) + req := ptyRequestMsg{ + Term: term, + Columns: uint32(w), + Rows: uint32(h), + Width: uint32(w * 8), + Height: uint32(h * 8), + Modelist: string(tm), + } + ok, err := s.ch.SendRequest("pty-req", true, Marshal(&req)) + if err == nil && !ok { + err = errors.New("ssh: pty-req failed") + } + return err +} + +// RFC 4254 Section 6.5. +type subsystemRequestMsg struct { + Subsystem string +} + +// RequestSubsystem requests the association of a subsystem with the session on the remote host. +// A subsystem is a predefined command that runs in the background when the ssh session is initiated +func (s *Session) RequestSubsystem(subsystem string) error { + msg := subsystemRequestMsg{ + Subsystem: subsystem, + } + ok, err := s.ch.SendRequest("subsystem", true, Marshal(&msg)) + if err == nil && !ok { + err = errors.New("ssh: subsystem request failed") + } + return err +} + +// RFC 4254 Section 6.9. +type signalMsg struct { + Signal string +} + +// Signal sends the given signal to the remote process. +// sig is one of the SIG* constants. +func (s *Session) Signal(sig Signal) error { + msg := signalMsg{ + Signal: string(sig), + } + + _, err := s.ch.SendRequest("signal", false, Marshal(&msg)) + return err +} + +// RFC 4254 Section 6.5. +type execMsg struct { + Command string +} + +// Start runs cmd on the remote host. Typically, the remote +// server passes cmd to the shell for interpretation. +// A Session only accepts one call to Run, Start or Shell. +func (s *Session) Start(cmd string) error { + if s.started { + return errors.New("ssh: session already started") + } + req := execMsg{ + Command: cmd, + } + + ok, err := s.ch.SendRequest("exec", true, Marshal(&req)) + if err == nil && !ok { + err = fmt.Errorf("ssh: command %v failed", cmd) + } + if err != nil { + return err + } + return s.start() +} + +// Run runs cmd on the remote host. Typically, the remote +// server passes cmd to the shell for interpretation. +// A Session only accepts one call to Run, Start, Shell, Output, +// or CombinedOutput. +// +// The returned error is nil if the command runs, has no problems +// copying stdin, stdout, and stderr, and exits with a zero exit +// status. +// +// If the remote server does not send an exit status, an error of type +// *ExitMissingError is returned. If the command completes +// unsuccessfully or is interrupted by a signal, the error is of type +// *ExitError. Other error types may be returned for I/O problems. +func (s *Session) Run(cmd string) error { + err := s.Start(cmd) + if err != nil { + return err + } + return s.Wait() +} + +// Output runs cmd on the remote host and returns its standard output. +func (s *Session) Output(cmd string) ([]byte, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + var b bytes.Buffer + s.Stdout = &b + err := s.Run(cmd) + return b.Bytes(), err +} + +type singleWriter struct { + b bytes.Buffer + mu sync.Mutex +} + +func (w *singleWriter) Write(p []byte) (int, error) { + w.mu.Lock() + defer w.mu.Unlock() + return w.b.Write(p) +} + +// CombinedOutput runs cmd on the remote host and returns its combined +// standard output and standard error. +func (s *Session) CombinedOutput(cmd string) ([]byte, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + if s.Stderr != nil { + return nil, errors.New("ssh: Stderr already set") + } + var b singleWriter + s.Stdout = &b + s.Stderr = &b + err := s.Run(cmd) + return b.b.Bytes(), err +} + +// Shell starts a login shell on the remote host. A Session only +// accepts one call to Run, Start, Shell, Output, or CombinedOutput. +func (s *Session) Shell() error { + if s.started { + return errors.New("ssh: session already started") + } + + ok, err := s.ch.SendRequest("shell", true, nil) + if err == nil && !ok { + return errors.New("ssh: could not start shell") + } + if err != nil { + return err + } + return s.start() +} + +func (s *Session) start() error { + s.started = true + + type F func(*Session) + for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} { + setupFd(s) + } + + s.errors = make(chan error, len(s.copyFuncs)) + for _, fn := range s.copyFuncs { + go func(fn func() error) { + s.errors <- fn() + }(fn) + } + return nil +} + +// Wait waits for the remote command to exit. +// +// The returned error is nil if the command runs, has no problems +// copying stdin, stdout, and stderr, and exits with a zero exit +// status. +// +// If the remote server does not send an exit status, an error of type +// *ExitMissingError is returned. If the command completes +// unsuccessfully or is interrupted by a signal, the error is of type +// *ExitError. Other error types may be returned for I/O problems. +func (s *Session) Wait() error { + if !s.started { + return errors.New("ssh: session not started") + } + waitErr := <-s.exitStatus + + if s.stdinPipeWriter != nil { + s.stdinPipeWriter.Close() + } + var copyError error + for _ = range s.copyFuncs { + if err := <-s.errors; err != nil && copyError == nil { + copyError = err + } + } + if waitErr != nil { + return waitErr + } + return copyError +} + +func (s *Session) wait(reqs <-chan *Request) error { + wm := Waitmsg{status: -1} + // Wait for msg channel to be closed before returning. + for msg := range reqs { + switch msg.Type { + case "exit-status": + wm.status = int(binary.BigEndian.Uint32(msg.Payload)) + case "exit-signal": + var sigval struct { + Signal string + CoreDumped bool + Error string + Lang string + } + if err := Unmarshal(msg.Payload, &sigval); err != nil { + return err + } + + // Must sanitize strings? + wm.signal = sigval.Signal + wm.msg = sigval.Error + wm.lang = sigval.Lang + default: + // This handles keepalives and matches + // OpenSSH's behaviour. + if msg.WantReply { + msg.Reply(false, nil) + } + } + } + if wm.status == 0 { + return nil + } + if wm.status == -1 { + // exit-status was never sent from server + if wm.signal == "" { + // signal was not sent either. RFC 4254 + // section 6.10 recommends against this + // behavior, but it is allowed, so we let + // clients handle it. + return &ExitMissingError{} + } + wm.status = 128 + if _, ok := signals[Signal(wm.signal)]; ok { + wm.status += signals[Signal(wm.signal)] + } + } + + return &ExitError{wm} +} + +// ExitMissingError is returned if a session is torn down cleanly, but +// the server sends no confirmation of the exit status. +type ExitMissingError struct{} + +func (e *ExitMissingError) Error() string { + return "wait: remote command exited without exit status or exit signal" +} + +func (s *Session) stdin() { + if s.stdinpipe { + return + } + var stdin io.Reader + if s.Stdin == nil { + stdin = new(bytes.Buffer) + } else { + r, w := io.Pipe() + go func() { + _, err := io.Copy(w, s.Stdin) + w.CloseWithError(err) + }() + stdin, s.stdinPipeWriter = r, w + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.ch, stdin) + if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF { + err = err1 + } + return err + }) +} + +func (s *Session) stdout() { + if s.stdoutpipe { + return + } + if s.Stdout == nil { + s.Stdout = ioutil.Discard + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.Stdout, s.ch) + return err + }) +} + +func (s *Session) stderr() { + if s.stderrpipe { + return + } + if s.Stderr == nil { + s.Stderr = ioutil.Discard + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.Stderr, s.ch.Stderr()) + return err + }) +} + +// sessionStdin reroutes Close to CloseWrite. +type sessionStdin struct { + io.Writer + ch Channel +} + +func (s *sessionStdin) Close() error { + return s.ch.CloseWrite() +} + +// StdinPipe returns a pipe that will be connected to the +// remote command's standard input when the command starts. +func (s *Session) StdinPipe() (io.WriteCloser, error) { + if s.Stdin != nil { + return nil, errors.New("ssh: Stdin already set") + } + if s.started { + return nil, errors.New("ssh: StdinPipe after process started") + } + s.stdinpipe = true + return &sessionStdin{s.ch, s.ch}, nil +} + +// StdoutPipe returns a pipe that will be connected to the +// remote command's standard output when the command starts. +// There is a fixed amount of buffering that is shared between +// stdout and stderr streams. If the StdoutPipe reader is +// not serviced fast enough it may eventually cause the +// remote command to block. +func (s *Session) StdoutPipe() (io.Reader, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + if s.started { + return nil, errors.New("ssh: StdoutPipe after process started") + } + s.stdoutpipe = true + return s.ch, nil +} + +// StderrPipe returns a pipe that will be connected to the +// remote command's standard error when the command starts. +// There is a fixed amount of buffering that is shared between +// stdout and stderr streams. If the StderrPipe reader is +// not serviced fast enough it may eventually cause the +// remote command to block. +func (s *Session) StderrPipe() (io.Reader, error) { + if s.Stderr != nil { + return nil, errors.New("ssh: Stderr already set") + } + if s.started { + return nil, errors.New("ssh: StderrPipe after process started") + } + s.stderrpipe = true + return s.ch.Stderr(), nil +} + +// newSession returns a new interactive session on the remote host. +func newSession(ch Channel, reqs <-chan *Request) (*Session, error) { + s := &Session{ + ch: ch, + } + s.exitStatus = make(chan error, 1) + go func() { + s.exitStatus <- s.wait(reqs) + }() + + return s, nil +} + +// An ExitError reports unsuccessful completion of a remote command. +type ExitError struct { + Waitmsg +} + +func (e *ExitError) Error() string { + return e.Waitmsg.String() +} + +// Waitmsg stores the information about an exited remote command +// as reported by Wait. +type Waitmsg struct { + status int + signal string + msg string + lang string +} + +// ExitStatus returns the exit status of the remote command. +func (w Waitmsg) ExitStatus() int { + return w.status +} + +// Signal returns the exit signal of the remote command if +// it was terminated violently. +func (w Waitmsg) Signal() string { + return w.signal +} + +// Msg returns the exit message given by the remote command +func (w Waitmsg) Msg() string { + return w.msg +} + +// Lang returns the language tag. See RFC 3066 +func (w Waitmsg) Lang() string { + return w.lang +} + +func (w Waitmsg) String() string { + str := fmt.Sprintf("Process exited with status %v", w.status) + if w.signal != "" { + str += fmt.Sprintf(" from signal %v", w.signal) + } + if w.msg != "" { + str += fmt.Sprintf(". Reason was: %v", w.msg) + } + return str +} diff --git a/vendor/golang.org/x/crypto/ssh/tcpip.go b/vendor/golang.org/x/crypto/ssh/tcpip.go new file mode 100644 index 000000000..6151241ff --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/tcpip.go @@ -0,0 +1,407 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "errors" + "fmt" + "io" + "math/rand" + "net" + "strconv" + "strings" + "sync" + "time" +) + +// Listen requests the remote peer open a listening socket on +// addr. Incoming connections will be available by calling Accept on +// the returned net.Listener. The listener must be serviced, or the +// SSH connection may hang. +func (c *Client) Listen(n, addr string) (net.Listener, error) { + laddr, err := net.ResolveTCPAddr(n, addr) + if err != nil { + return nil, err + } + return c.ListenTCP(laddr) +} + +// Automatic port allocation is broken with OpenSSH before 6.0. See +// also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In +// particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0, +// rather than the actual port number. This means you can never open +// two different listeners with auto allocated ports. We work around +// this by trying explicit ports until we succeed. + +const openSSHPrefix = "OpenSSH_" + +var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano())) + +// isBrokenOpenSSHVersion returns true if the given version string +// specifies a version of OpenSSH that is known to have a bug in port +// forwarding. +func isBrokenOpenSSHVersion(versionStr string) bool { + i := strings.Index(versionStr, openSSHPrefix) + if i < 0 { + return false + } + i += len(openSSHPrefix) + j := i + for ; j < len(versionStr); j++ { + if versionStr[j] < '0' || versionStr[j] > '9' { + break + } + } + version, _ := strconv.Atoi(versionStr[i:j]) + return version < 6 +} + +// autoPortListenWorkaround simulates automatic port allocation by +// trying random ports repeatedly. +func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) { + var sshListener net.Listener + var err error + const tries = 10 + for i := 0; i < tries; i++ { + addr := *laddr + addr.Port = 1024 + portRandomizer.Intn(60000) + sshListener, err = c.ListenTCP(&addr) + if err == nil { + laddr.Port = addr.Port + return sshListener, err + } + } + return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err) +} + +// RFC 4254 7.1 +type channelForwardMsg struct { + addr string + rport uint32 +} + +// ListenTCP requests the remote peer open a listening socket +// on laddr. Incoming connections will be available by calling +// Accept on the returned net.Listener. +func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { + if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) { + return c.autoPortListenWorkaround(laddr) + } + + m := channelForwardMsg{ + laddr.IP.String(), + uint32(laddr.Port), + } + // send message + ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m)) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("ssh: tcpip-forward request denied by peer") + } + + // If the original port was 0, then the remote side will + // supply a real port number in the response. + if laddr.Port == 0 { + var p struct { + Port uint32 + } + if err := Unmarshal(resp, &p); err != nil { + return nil, err + } + laddr.Port = int(p.Port) + } + + // Register this forward, using the port number we obtained. + ch := c.forwards.add(*laddr) + + return &tcpListener{laddr, c, ch}, nil +} + +// forwardList stores a mapping between remote +// forward requests and the tcpListeners. +type forwardList struct { + sync.Mutex + entries []forwardEntry +} + +// forwardEntry represents an established mapping of a laddr on a +// remote ssh server to a channel connected to a tcpListener. +type forwardEntry struct { + laddr net.TCPAddr + c chan forward +} + +// forward represents an incoming forwarded tcpip connection. The +// arguments to add/remove/lookup should be address as specified in +// the original forward-request. +type forward struct { + newCh NewChannel // the ssh client channel underlying this forward + raddr *net.TCPAddr // the raddr of the incoming connection +} + +func (l *forwardList) add(addr net.TCPAddr) chan forward { + l.Lock() + defer l.Unlock() + f := forwardEntry{ + addr, + make(chan forward, 1), + } + l.entries = append(l.entries, f) + return f.c +} + +// See RFC 4254, section 7.2 +type forwardedTCPPayload struct { + Addr string + Port uint32 + OriginAddr string + OriginPort uint32 +} + +// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr. +func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) { + if port == 0 || port > 65535 { + return nil, fmt.Errorf("ssh: port number out of range: %d", port) + } + ip := net.ParseIP(string(addr)) + if ip == nil { + return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr) + } + return &net.TCPAddr{IP: ip, Port: int(port)}, nil +} + +func (l *forwardList) handleChannels(in <-chan NewChannel) { + for ch := range in { + var payload forwardedTCPPayload + if err := Unmarshal(ch.ExtraData(), &payload); err != nil { + ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) + continue + } + + // RFC 4254 section 7.2 specifies that incoming + // addresses should list the address, in string + // format. It is implied that this should be an IP + // address, as it would be impossible to connect to it + // otherwise. + laddr, err := parseTCPAddr(payload.Addr, payload.Port) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + + if ok := l.forward(*laddr, *raddr, ch); !ok { + // Section 7.2, implementations MUST reject spurious incoming + // connections. + ch.Reject(Prohibited, "no forward for address") + continue + } + } +} + +// remove removes the forward entry, and the channel feeding its +// listener. +func (l *forwardList) remove(addr net.TCPAddr) { + l.Lock() + defer l.Unlock() + for i, f := range l.entries { + if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { + l.entries = append(l.entries[:i], l.entries[i+1:]...) + close(f.c) + return + } + } +} + +// closeAll closes and clears all forwards. +func (l *forwardList) closeAll() { + l.Lock() + defer l.Unlock() + for _, f := range l.entries { + close(f.c) + } + l.entries = nil +} + +func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool { + l.Lock() + defer l.Unlock() + for _, f := range l.entries { + if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port { + f.c <- forward{ch, &raddr} + return true + } + } + return false +} + +type tcpListener struct { + laddr *net.TCPAddr + + conn *Client + in <-chan forward +} + +// Accept waits for and returns the next connection to the listener. +func (l *tcpListener) Accept() (net.Conn, error) { + s, ok := <-l.in + if !ok { + return nil, io.EOF + } + ch, incoming, err := s.newCh.Accept() + if err != nil { + return nil, err + } + go DiscardRequests(incoming) + + return &tcpChanConn{ + Channel: ch, + laddr: l.laddr, + raddr: s.raddr, + }, nil +} + +// Close closes the listener. +func (l *tcpListener) Close() error { + m := channelForwardMsg{ + l.laddr.IP.String(), + uint32(l.laddr.Port), + } + + // this also closes the listener. + l.conn.forwards.remove(*l.laddr) + ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m)) + if err == nil && !ok { + err = errors.New("ssh: cancel-tcpip-forward failed") + } + return err +} + +// Addr returns the listener's network address. +func (l *tcpListener) Addr() net.Addr { + return l.laddr +} + +// Dial initiates a connection to the addr from the remote host. +// The resulting connection has a zero LocalAddr() and RemoteAddr(). +func (c *Client) Dial(n, addr string) (net.Conn, error) { + // Parse the address into host and numeric port. + host, portString, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + port, err := strconv.ParseUint(portString, 10, 16) + if err != nil { + return nil, err + } + // Use a zero address for local and remote address. + zeroAddr := &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port)) + if err != nil { + return nil, err + } + return &tcpChanConn{ + Channel: ch, + laddr: zeroAddr, + raddr: zeroAddr, + }, nil +} + +// DialTCP connects to the remote address raddr on the network net, +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +// as the local address for the connection. +func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { + if laddr == nil { + laddr = &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + } + ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) + if err != nil { + return nil, err + } + return &tcpChanConn{ + Channel: ch, + laddr: laddr, + raddr: raddr, + }, nil +} + +// RFC 4254 7.2 +type channelOpenDirectMsg struct { + raddr string + rport uint32 + laddr string + lport uint32 +} + +func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) { + msg := channelOpenDirectMsg{ + raddr: raddr, + rport: uint32(rport), + laddr: laddr, + lport: uint32(lport), + } + ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg)) + if err != nil { + return nil, err + } + go DiscardRequests(in) + return ch, err +} + +type tcpChan struct { + Channel // the backing channel +} + +// tcpChanConn fulfills the net.Conn interface without +// the tcpChan having to hold laddr or raddr directly. +type tcpChanConn struct { + Channel + laddr, raddr net.Addr +} + +// LocalAddr returns the local network address. +func (t *tcpChanConn) LocalAddr() net.Addr { + return t.laddr +} + +// RemoteAddr returns the remote network address. +func (t *tcpChanConn) RemoteAddr() net.Addr { + return t.raddr +} + +// SetDeadline sets the read and write deadlines associated +// with the connection. +func (t *tcpChanConn) SetDeadline(deadline time.Time) error { + if err := t.SetReadDeadline(deadline); err != nil { + return err + } + return t.SetWriteDeadline(deadline) +} + +// SetReadDeadline sets the read deadline. +// A zero value for t means Read will not time out. +// After the deadline, the error from Read will implement net.Error +// with Timeout() == true. +func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { + return errors.New("ssh: tcpChan: deadline not supported") +} + +// SetWriteDeadline exists to satisfy the net.Conn interface +// but is not implemented by this type. It always returns an error. +func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { + return errors.New("ssh: tcpChan: deadline not supported") +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go new file mode 100644 index 000000000..18379a935 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go @@ -0,0 +1,951 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package terminal + +import ( + "bytes" + "io" + "sync" + "unicode/utf8" +) + +// EscapeCodes contains escape sequences that can be written to the terminal in +// order to achieve different styles of text. +type EscapeCodes struct { + // Foreground colors + Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte + + // Reset all attributes + Reset []byte +} + +var vt100EscapeCodes = EscapeCodes{ + Black: []byte{keyEscape, '[', '3', '0', 'm'}, + Red: []byte{keyEscape, '[', '3', '1', 'm'}, + Green: []byte{keyEscape, '[', '3', '2', 'm'}, + Yellow: []byte{keyEscape, '[', '3', '3', 'm'}, + Blue: []byte{keyEscape, '[', '3', '4', 'm'}, + Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, + Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, + White: []byte{keyEscape, '[', '3', '7', 'm'}, + + Reset: []byte{keyEscape, '[', '0', 'm'}, +} + +// Terminal contains the state for running a VT100 terminal that is capable of +// reading lines of input. +type Terminal struct { + // AutoCompleteCallback, if non-null, is called for each keypress with + // the full input line and the current position of the cursor (in + // bytes, as an index into |line|). If it returns ok=false, the key + // press is processed normally. Otherwise it returns a replacement line + // and the new cursor position. + AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) + + // Escape contains a pointer to the escape codes for this terminal. + // It's always a valid pointer, although the escape codes themselves + // may be empty if the terminal doesn't support them. + Escape *EscapeCodes + + // lock protects the terminal and the state in this object from + // concurrent processing of a key press and a Write() call. + lock sync.Mutex + + c io.ReadWriter + prompt []rune + + // line is the current line being entered. + line []rune + // pos is the logical position of the cursor in line + pos int + // echo is true if local echo is enabled + echo bool + // pasteActive is true iff there is a bracketed paste operation in + // progress. + pasteActive bool + + // cursorX contains the current X value of the cursor where the left + // edge is 0. cursorY contains the row number where the first row of + // the current line is 0. + cursorX, cursorY int + // maxLine is the greatest value of cursorY so far. + maxLine int + + termWidth, termHeight int + + // outBuf contains the terminal data to be sent. + outBuf []byte + // remainder contains the remainder of any partial key sequences after + // a read. It aliases into inBuf. + remainder []byte + inBuf [256]byte + + // history contains previously entered commands so that they can be + // accessed with the up and down keys. + history stRingBuffer + // historyIndex stores the currently accessed history entry, where zero + // means the immediately previous entry. + historyIndex int + // When navigating up and down the history it's possible to return to + // the incomplete, initial line. That value is stored in + // historyPending. + historyPending string +} + +// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is +// a local terminal, that terminal must first have been put into raw mode. +// prompt is a string that is written at the start of each input line (i.e. +// "> "). +func NewTerminal(c io.ReadWriter, prompt string) *Terminal { + return &Terminal{ + Escape: &vt100EscapeCodes, + c: c, + prompt: []rune(prompt), + termWidth: 80, + termHeight: 24, + echo: true, + historyIndex: -1, + } +} + +const ( + keyCtrlD = 4 + keyCtrlU = 21 + keyEnter = '\r' + keyEscape = 27 + keyBackspace = 127 + keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota + keyUp + keyDown + keyLeft + keyRight + keyAltLeft + keyAltRight + keyHome + keyEnd + keyDeleteWord + keyDeleteLine + keyClearScreen + keyPasteStart + keyPasteEnd +) + +var ( + crlf = []byte{'\r', '\n'} + pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} + pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} +) + +// bytesToKey tries to parse a key sequence from b. If successful, it returns +// the key and the remainder of the input. Otherwise it returns utf8.RuneError. +func bytesToKey(b []byte, pasteActive bool) (rune, []byte) { + if len(b) == 0 { + return utf8.RuneError, nil + } + + if !pasteActive { + switch b[0] { + case 1: // ^A + return keyHome, b[1:] + case 5: // ^E + return keyEnd, b[1:] + case 8: // ^H + return keyBackspace, b[1:] + case 11: // ^K + return keyDeleteLine, b[1:] + case 12: // ^L + return keyClearScreen, b[1:] + case 23: // ^W + return keyDeleteWord, b[1:] + } + } + + if b[0] != keyEscape { + if !utf8.FullRune(b) { + return utf8.RuneError, b + } + r, l := utf8.DecodeRune(b) + return r, b[l:] + } + + if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { + switch b[2] { + case 'A': + return keyUp, b[3:] + case 'B': + return keyDown, b[3:] + case 'C': + return keyRight, b[3:] + case 'D': + return keyLeft, b[3:] + case 'H': + return keyHome, b[3:] + case 'F': + return keyEnd, b[3:] + } + } + + if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { + switch b[5] { + case 'C': + return keyAltRight, b[6:] + case 'D': + return keyAltLeft, b[6:] + } + } + + if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) { + return keyPasteStart, b[6:] + } + + if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) { + return keyPasteEnd, b[6:] + } + + // If we get here then we have a key that we don't recognise, or a + // partial sequence. It's not clear how one should find the end of a + // sequence without knowing them all, but it seems that [a-zA-Z~] only + // appears at the end of a sequence. + for i, c := range b[0:] { + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' { + return keyUnknown, b[i+1:] + } + } + + return utf8.RuneError, b +} + +// queue appends data to the end of t.outBuf +func (t *Terminal) queue(data []rune) { + t.outBuf = append(t.outBuf, []byte(string(data))...) +} + +var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} +var space = []rune{' '} + +func isPrintable(key rune) bool { + isInSurrogateArea := key >= 0xd800 && key <= 0xdbff + return key >= 32 && !isInSurrogateArea +} + +// moveCursorToPos appends data to t.outBuf which will move the cursor to the +// given, logical position in the text. +func (t *Terminal) moveCursorToPos(pos int) { + if !t.echo { + return + } + + x := visualLength(t.prompt) + pos + y := x / t.termWidth + x = x % t.termWidth + + up := 0 + if y < t.cursorY { + up = t.cursorY - y + } + + down := 0 + if y > t.cursorY { + down = y - t.cursorY + } + + left := 0 + if x < t.cursorX { + left = t.cursorX - x + } + + right := 0 + if x > t.cursorX { + right = x - t.cursorX + } + + t.cursorX = x + t.cursorY = y + t.move(up, down, left, right) +} + +func (t *Terminal) move(up, down, left, right int) { + movement := make([]rune, 3*(up+down+left+right)) + m := movement + for i := 0; i < up; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'A' + m = m[3:] + } + for i := 0; i < down; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'B' + m = m[3:] + } + for i := 0; i < left; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'D' + m = m[3:] + } + for i := 0; i < right; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'C' + m = m[3:] + } + + t.queue(movement) +} + +func (t *Terminal) clearLineToRight() { + op := []rune{keyEscape, '[', 'K'} + t.queue(op) +} + +const maxLineLength = 4096 + +func (t *Terminal) setLine(newLine []rune, newPos int) { + if t.echo { + t.moveCursorToPos(0) + t.writeLine(newLine) + for i := len(newLine); i < len(t.line); i++ { + t.writeLine(space) + } + t.moveCursorToPos(newPos) + } + t.line = newLine + t.pos = newPos +} + +func (t *Terminal) advanceCursor(places int) { + t.cursorX += places + t.cursorY += t.cursorX / t.termWidth + if t.cursorY > t.maxLine { + t.maxLine = t.cursorY + } + t.cursorX = t.cursorX % t.termWidth + + if places > 0 && t.cursorX == 0 { + // Normally terminals will advance the current position + // when writing a character. But that doesn't happen + // for the last character in a line. However, when + // writing a character (except a new line) that causes + // a line wrap, the position will be advanced two + // places. + // + // So, if we are stopping at the end of a line, we + // need to write a newline so that our cursor can be + // advanced to the next line. + t.outBuf = append(t.outBuf, '\r', '\n') + } +} + +func (t *Terminal) eraseNPreviousChars(n int) { + if n == 0 { + return + } + + if t.pos < n { + n = t.pos + } + t.pos -= n + t.moveCursorToPos(t.pos) + + copy(t.line[t.pos:], t.line[n+t.pos:]) + t.line = t.line[:len(t.line)-n] + if t.echo { + t.writeLine(t.line[t.pos:]) + for i := 0; i < n; i++ { + t.queue(space) + } + t.advanceCursor(n) + t.moveCursorToPos(t.pos) + } +} + +// countToLeftWord returns then number of characters from the cursor to the +// start of the previous word. +func (t *Terminal) countToLeftWord() int { + if t.pos == 0 { + return 0 + } + + pos := t.pos - 1 + for pos > 0 { + if t.line[pos] != ' ' { + break + } + pos-- + } + for pos > 0 { + if t.line[pos] == ' ' { + pos++ + break + } + pos-- + } + + return t.pos - pos +} + +// countToRightWord returns then number of characters from the cursor to the +// start of the next word. +func (t *Terminal) countToRightWord() int { + pos := t.pos + for pos < len(t.line) { + if t.line[pos] == ' ' { + break + } + pos++ + } + for pos < len(t.line) { + if t.line[pos] != ' ' { + break + } + pos++ + } + return pos - t.pos +} + +// visualLength returns the number of visible glyphs in s. +func visualLength(runes []rune) int { + inEscapeSeq := false + length := 0 + + for _, r := range runes { + switch { + case inEscapeSeq: + if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') { + inEscapeSeq = false + } + case r == '\x1b': + inEscapeSeq = true + default: + length++ + } + } + + return length +} + +// handleKey processes the given key and, optionally, returns a line of text +// that the user has entered. +func (t *Terminal) handleKey(key rune) (line string, ok bool) { + if t.pasteActive && key != keyEnter { + t.addKeyToLine(key) + return + } + + switch key { + case keyBackspace: + if t.pos == 0 { + return + } + t.eraseNPreviousChars(1) + case keyAltLeft: + // move left by a word. + t.pos -= t.countToLeftWord() + t.moveCursorToPos(t.pos) + case keyAltRight: + // move right by a word. + t.pos += t.countToRightWord() + t.moveCursorToPos(t.pos) + case keyLeft: + if t.pos == 0 { + return + } + t.pos-- + t.moveCursorToPos(t.pos) + case keyRight: + if t.pos == len(t.line) { + return + } + t.pos++ + t.moveCursorToPos(t.pos) + case keyHome: + if t.pos == 0 { + return + } + t.pos = 0 + t.moveCursorToPos(t.pos) + case keyEnd: + if t.pos == len(t.line) { + return + } + t.pos = len(t.line) + t.moveCursorToPos(t.pos) + case keyUp: + entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) + if !ok { + return "", false + } + if t.historyIndex == -1 { + t.historyPending = string(t.line) + } + t.historyIndex++ + runes := []rune(entry) + t.setLine(runes, len(runes)) + case keyDown: + switch t.historyIndex { + case -1: + return + case 0: + runes := []rune(t.historyPending) + t.setLine(runes, len(runes)) + t.historyIndex-- + default: + entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) + if ok { + t.historyIndex-- + runes := []rune(entry) + t.setLine(runes, len(runes)) + } + } + case keyEnter: + t.moveCursorToPos(len(t.line)) + t.queue([]rune("\r\n")) + line = string(t.line) + ok = true + t.line = t.line[:0] + t.pos = 0 + t.cursorX = 0 + t.cursorY = 0 + t.maxLine = 0 + case keyDeleteWord: + // Delete zero or more spaces and then one or more characters. + t.eraseNPreviousChars(t.countToLeftWord()) + case keyDeleteLine: + // Delete everything from the current cursor position to the + // end of line. + for i := t.pos; i < len(t.line); i++ { + t.queue(space) + t.advanceCursor(1) + } + t.line = t.line[:t.pos] + t.moveCursorToPos(t.pos) + case keyCtrlD: + // Erase the character under the current position. + // The EOF case when the line is empty is handled in + // readLine(). + if t.pos < len(t.line) { + t.pos++ + t.eraseNPreviousChars(1) + } + case keyCtrlU: + t.eraseNPreviousChars(t.pos) + case keyClearScreen: + // Erases the screen and moves the cursor to the home position. + t.queue([]rune("\x1b[2J\x1b[H")) + t.queue(t.prompt) + t.cursorX, t.cursorY = 0, 0 + t.advanceCursor(visualLength(t.prompt)) + t.setLine(t.line, t.pos) + default: + if t.AutoCompleteCallback != nil { + prefix := string(t.line[:t.pos]) + suffix := string(t.line[t.pos:]) + + t.lock.Unlock() + newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key) + t.lock.Lock() + + if completeOk { + t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos])) + return + } + } + if !isPrintable(key) { + return + } + if len(t.line) == maxLineLength { + return + } + t.addKeyToLine(key) + } + return +} + +// addKeyToLine inserts the given key at the current position in the current +// line. +func (t *Terminal) addKeyToLine(key rune) { + if len(t.line) == cap(t.line) { + newLine := make([]rune, len(t.line), 2*(1+len(t.line))) + copy(newLine, t.line) + t.line = newLine + } + t.line = t.line[:len(t.line)+1] + copy(t.line[t.pos+1:], t.line[t.pos:]) + t.line[t.pos] = key + if t.echo { + t.writeLine(t.line[t.pos:]) + } + t.pos++ + t.moveCursorToPos(t.pos) +} + +func (t *Terminal) writeLine(line []rune) { + for len(line) != 0 { + remainingOnLine := t.termWidth - t.cursorX + todo := len(line) + if todo > remainingOnLine { + todo = remainingOnLine + } + t.queue(line[:todo]) + t.advanceCursor(visualLength(line[:todo])) + line = line[todo:] + } +} + +// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n. +func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) { + for len(buf) > 0 { + i := bytes.IndexByte(buf, '\n') + todo := len(buf) + if i >= 0 { + todo = i + } + + var nn int + nn, err = w.Write(buf[:todo]) + n += nn + if err != nil { + return n, err + } + buf = buf[todo:] + + if i >= 0 { + if _, err = w.Write(crlf); err != nil { + return n, err + } + n += 1 + buf = buf[1:] + } + } + + return n, nil +} + +func (t *Terminal) Write(buf []byte) (n int, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + if t.cursorX == 0 && t.cursorY == 0 { + // This is the easy case: there's nothing on the screen that we + // have to move out of the way. + return writeWithCRLF(t.c, buf) + } + + // We have a prompt and possibly user input on the screen. We + // have to clear it first. + t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) + t.cursorX = 0 + t.clearLineToRight() + + for t.cursorY > 0 { + t.move(1 /* up */, 0, 0, 0) + t.cursorY-- + t.clearLineToRight() + } + + if _, err = t.c.Write(t.outBuf); err != nil { + return + } + t.outBuf = t.outBuf[:0] + + if n, err = writeWithCRLF(t.c, buf); err != nil { + return + } + + t.writeLine(t.prompt) + if t.echo { + t.writeLine(t.line) + } + + t.moveCursorToPos(t.pos) + + if _, err = t.c.Write(t.outBuf); err != nil { + return + } + t.outBuf = t.outBuf[:0] + return +} + +// ReadPassword temporarily changes the prompt and reads a password, without +// echo, from the terminal. +func (t *Terminal) ReadPassword(prompt string) (line string, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + oldPrompt := t.prompt + t.prompt = []rune(prompt) + t.echo = false + + line, err = t.readLine() + + t.prompt = oldPrompt + t.echo = true + + return +} + +// ReadLine returns a line of input from the terminal. +func (t *Terminal) ReadLine() (line string, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + return t.readLine() +} + +func (t *Terminal) readLine() (line string, err error) { + // t.lock must be held at this point + + if t.cursorX == 0 && t.cursorY == 0 { + t.writeLine(t.prompt) + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + } + + lineIsPasted := t.pasteActive + + for { + rest := t.remainder + lineOk := false + for !lineOk { + var key rune + key, rest = bytesToKey(rest, t.pasteActive) + if key == utf8.RuneError { + break + } + if !t.pasteActive { + if key == keyCtrlD { + if len(t.line) == 0 { + return "", io.EOF + } + } + if key == keyPasteStart { + t.pasteActive = true + if len(t.line) == 0 { + lineIsPasted = true + } + continue + } + } else if key == keyPasteEnd { + t.pasteActive = false + continue + } + if !t.pasteActive { + lineIsPasted = false + } + line, lineOk = t.handleKey(key) + } + if len(rest) > 0 { + n := copy(t.inBuf[:], rest) + t.remainder = t.inBuf[:n] + } else { + t.remainder = nil + } + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + if lineOk { + if t.echo { + t.historyIndex = -1 + t.history.Add(line) + } + if lineIsPasted { + err = ErrPasteIndicator + } + return + } + + // t.remainder is a slice at the beginning of t.inBuf + // containing a partial key sequence + readBuf := t.inBuf[len(t.remainder):] + var n int + + t.lock.Unlock() + n, err = t.c.Read(readBuf) + t.lock.Lock() + + if err != nil { + return + } + + t.remainder = t.inBuf[:n+len(t.remainder)] + } +} + +// SetPrompt sets the prompt to be used when reading subsequent lines. +func (t *Terminal) SetPrompt(prompt string) { + t.lock.Lock() + defer t.lock.Unlock() + + t.prompt = []rune(prompt) +} + +func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) { + // Move cursor to column zero at the start of the line. + t.move(t.cursorY, 0, t.cursorX, 0) + t.cursorX, t.cursorY = 0, 0 + t.clearLineToRight() + for t.cursorY < numPrevLines { + // Move down a line + t.move(0, 1, 0, 0) + t.cursorY++ + t.clearLineToRight() + } + // Move back to beginning. + t.move(t.cursorY, 0, 0, 0) + t.cursorX, t.cursorY = 0, 0 + + t.queue(t.prompt) + t.advanceCursor(visualLength(t.prompt)) + t.writeLine(t.line) + t.moveCursorToPos(t.pos) +} + +func (t *Terminal) SetSize(width, height int) error { + t.lock.Lock() + defer t.lock.Unlock() + + if width == 0 { + width = 1 + } + + oldWidth := t.termWidth + t.termWidth, t.termHeight = width, height + + switch { + case width == oldWidth: + // If the width didn't change then nothing else needs to be + // done. + return nil + case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0: + // If there is nothing on current line and no prompt printed, + // just do nothing + return nil + case width < oldWidth: + // Some terminals (e.g. xterm) will truncate lines that were + // too long when shinking. Others, (e.g. gnome-terminal) will + // attempt to wrap them. For the former, repainting t.maxLine + // works great, but that behaviour goes badly wrong in the case + // of the latter because they have doubled every full line. + + // We assume that we are working on a terminal that wraps lines + // and adjust the cursor position based on every previous line + // wrapping and turning into two. This causes the prompt on + // xterms to move upwards, which isn't great, but it avoids a + // huge mess with gnome-terminal. + if t.cursorX >= t.termWidth { + t.cursorX = t.termWidth - 1 + } + t.cursorY *= 2 + t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2) + case width > oldWidth: + // If the terminal expands then our position calculations will + // be wrong in the future because we think the cursor is + // |t.pos| chars into the string, but there will be a gap at + // the end of any wrapped line. + // + // But the position will actually be correct until we move, so + // we can move back to the beginning and repaint everything. + t.clearAndRepaintLinePlusNPrevious(t.maxLine) + } + + _, err := t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + return err +} + +type pasteIndicatorError struct{} + +func (pasteIndicatorError) Error() string { + return "terminal: ErrPasteIndicator not correctly handled" +} + +// ErrPasteIndicator may be returned from ReadLine as the error, in addition +// to valid line data. It indicates that bracketed paste mode is enabled and +// that the returned line consists only of pasted data. Programs may wish to +// interpret pasted data more literally than typed data. +var ErrPasteIndicator = pasteIndicatorError{} + +// SetBracketedPasteMode requests that the terminal bracket paste operations +// with markers. Not all terminals support this but, if it is supported, then +// enabling this mode will stop any autocomplete callback from running due to +// pastes. Additionally, any lines that are completely pasted will be returned +// from ReadLine with the error set to ErrPasteIndicator. +func (t *Terminal) SetBracketedPasteMode(on bool) { + if on { + io.WriteString(t.c, "\x1b[?2004h") + } else { + io.WriteString(t.c, "\x1b[?2004l") + } +} + +// stRingBuffer is a ring buffer of strings. +type stRingBuffer struct { + // entries contains max elements. + entries []string + max int + // head contains the index of the element most recently added to the ring. + head int + // size contains the number of elements in the ring. + size int +} + +func (s *stRingBuffer) Add(a string) { + if s.entries == nil { + const defaultNumEntries = 100 + s.entries = make([]string, defaultNumEntries) + s.max = defaultNumEntries + } + + s.head = (s.head + 1) % s.max + s.entries[s.head] = a + if s.size < s.max { + s.size++ + } +} + +// NthPreviousEntry returns the value passed to the nth previous call to Add. +// If n is zero then the immediately prior value is returned, if one, then the +// next most recent, and so on. If such an element doesn't exist then ok is +// false. +func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { + if n >= s.size { + return "", false + } + index := s.head - n + if index < 0 { + index += s.max + } + return s.entries[index], true +} + +// readPasswordLine reads from reader until it finds \n or io.EOF. +// The slice returned does not include the \n. +// readPasswordLine also ignores any \r it finds. +func readPasswordLine(reader io.Reader) ([]byte, error) { + var buf [1]byte + var ret []byte + + for { + n, err := reader.Read(buf[:]) + if n > 0 { + switch buf[0] { + case '\n': + return ret, nil + case '\r': + // remove \r from passwords on Windows + default: + ret = append(ret, buf[0]) + } + continue + } + if err != nil { + if err == io.EOF && len(ret) > 0 { + return ret, nil + } + return ret, err + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go new file mode 100644 index 000000000..d01919614 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go @@ -0,0 +1,119 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err) +// } +// defer terminal.Restore(0, oldState) +package terminal // import "golang.org/x/crypto/ssh/terminal" + +import ( + "syscall" + "unsafe" +) + +// State contains the state of a terminal. +type State struct { + termios syscall.Termios +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + return nil, err + } + + newState := oldState.termios + // This attempts to replicate the behaviour documented for cfmakeraw in + // the termios(3) manpage. + newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON + newState.Oflag &^= syscall.OPOST + newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN + newState.Cflag &^= syscall.CSIZE | syscall.PARENB + newState.Cflag |= syscall.CS8 + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + return nil, err + } + + return &oldState, nil +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + return nil, err + } + + return &oldState, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0); err != 0 { + return err + } + return nil +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + var dimensions [4]uint16 + + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { + return -1, -1, err + } + return int(dimensions[1]), int(dimensions[0]), nil +} + +// passwordReader is an io.Reader that reads from a specific file descriptor. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return syscall.Read(int(r), buf) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + var oldState syscall.Termios + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { + return nil, err + } + + newState := oldState + newState.Lflag &^= syscall.ECHO + newState.Lflag |= syscall.ICANON | syscall.ISIG + newState.Iflag |= syscall.ICRNL + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + return nil, err + } + + defer func() { + syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) + }() + + return readPasswordLine(passwordReader(fd)) +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go new file mode 100644 index 000000000..9c1ffd145 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go @@ -0,0 +1,12 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package terminal + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA +const ioctlWriteTermios = syscall.TIOCSETA diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go new file mode 100644 index 000000000..5883b22d7 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go @@ -0,0 +1,11 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package terminal + +// These constants are declared here, rather than importing +// them from the syscall package as some syscall packages, even +// on linux, for example gccgo, do not declare them. +const ioctlReadTermios = 0x5401 // syscall.TCGETS +const ioctlWriteTermios = 0x5402 // syscall.TCSETS diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go new file mode 100644 index 000000000..799f049f0 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go @@ -0,0 +1,58 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err) +// } +// defer terminal.Restore(0, oldState) +package terminal + +import ( + "fmt" + "runtime" +) + +type State struct{} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + return false +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go new file mode 100644 index 000000000..07eb5edd7 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go @@ -0,0 +1,73 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build solaris + +package terminal // import "golang.org/x/crypto/ssh/terminal" + +import ( + "golang.org/x/sys/unix" + "io" + "syscall" +) + +// State contains the state of a terminal. +type State struct { + termios syscall.Termios +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + // see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c + var termio unix.Termio + err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio) + return err == nil +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + // see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c + val, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return nil, err + } + oldState := *val + + newState := oldState + newState.Lflag &^= syscall.ECHO + newState.Lflag |= syscall.ICANON | syscall.ISIG + newState.Iflag |= syscall.ICRNL + err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState) + if err != nil { + return nil, err + } + + defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState) + + var buf [16]byte + var ret []byte + for { + n, err := syscall.Read(fd, buf[:]) + if err != nil { + return nil, err + } + if n == 0 { + if len(ret) == 0 { + return nil, io.EOF + } + break + } + if buf[n-1] == '\n' { + n-- + } + ret = append(ret, buf[:n]...) + if n < len(buf) { + break + } + } + + return ret, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go new file mode 100644 index 000000000..e0a1f36ce --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go @@ -0,0 +1,155 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err) +// } +// defer terminal.Restore(0, oldState) +package terminal + +import ( + "syscall" + "unsafe" +) + +const ( + enableLineInput = 2 + enableEchoInput = 4 + enableProcessedInput = 1 + enableWindowInput = 8 + enableMouseInput = 16 + enableInsertMode = 32 + enableQuickEditMode = 64 + enableExtendedFlags = 128 + enableAutoPosition = 256 + enableProcessedOutput = 1 + enableWrapAtEolOutput = 2 +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") +) + +type ( + short int16 + word uint16 + + coord struct { + x short + y short + } + smallRect struct { + left short + top short + right short + bottom short + } + consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord + } +) + +type State struct { + mode uint32 +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) + _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0) + if e != 0 { + return nil, error(e) + } + return &State{st}, nil +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + return &State{st}, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0) + return err +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + var info consoleScreenBufferInfo + _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0) + if e != 0 { + return 0, 0, error(e) + } + return int(info.size.x), int(info.size.y), nil +} + +// passwordReader is an io.Reader that reads from a specific Windows HANDLE. +type passwordReader int + +func (r passwordReader) Read(buf []byte) (int, error) { + return syscall.Read(syscall.Handle(r), buf) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + old := st + + st &^= (enableEchoInput) + st |= (enableProcessedInput | enableLineInput | enableProcessedOutput) + _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) + if e != 0 { + return nil, error(e) + } + + defer func() { + syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0) + }() + + return readPasswordLine(passwordReader(fd)) +} diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go new file mode 100644 index 000000000..f9780e0ae --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -0,0 +1,375 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bufio" + "errors" + "io" + "log" +) + +// debugTransport if set, will print packet types as they go over the +// wire. No message decoding is done, to minimize the impact on timing. +const debugTransport = false + +const ( + gcmCipherID = "aes128-gcm@openssh.com" + aes128cbcID = "aes128-cbc" + tripledescbcID = "3des-cbc" +) + +// packetConn represents a transport that implements packet based +// operations. +type packetConn interface { + // Encrypt and send a packet of data to the remote peer. + writePacket(packet []byte) error + + // Read a packet from the connection. The read is blocking, + // i.e. if error is nil, then the returned byte slice is + // always non-empty. + readPacket() ([]byte, error) + + // Close closes the write-side of the connection. + Close() error +} + +// transport is the keyingTransport that implements the SSH packet +// protocol. +type transport struct { + reader connectionState + writer connectionState + + bufReader *bufio.Reader + bufWriter *bufio.Writer + rand io.Reader + isClient bool + io.Closer +} + +// packetCipher represents a combination of SSH encryption/MAC +// protocol. A single instance should be used for one direction only. +type packetCipher interface { + // writePacket encrypts the packet and writes it to w. The + // contents of the packet are generally scrambled. + writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error + + // readPacket reads and decrypts a packet of data. The + // returned packet may be overwritten by future calls of + // readPacket. + readPacket(seqnum uint32, r io.Reader) ([]byte, error) +} + +// connectionState represents one side (read or write) of the +// connection. This is necessary because each direction has its own +// keys, and can even have its own algorithms +type connectionState struct { + packetCipher + seqNum uint32 + dir direction + pendingKeyChange chan packetCipher +} + +// prepareKeyChange sets up key material for a keychange. The key changes in +// both directions are triggered by reading and writing a msgNewKey packet +// respectively. +func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { + if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { + return err + } else { + t.reader.pendingKeyChange <- ciph + } + + if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil { + return err + } else { + t.writer.pendingKeyChange <- ciph + } + + return nil +} + +func (t *transport) printPacket(p []byte, write bool) { + if len(p) == 0 { + return + } + who := "server" + if t.isClient { + who = "client" + } + what := "read" + if write { + what = "write" + } + + log.Println(what, who, p[0]) +} + +// Read and decrypt next packet. +func (t *transport) readPacket() (p []byte, err error) { + for { + p, err = t.reader.readPacket(t.bufReader) + if err != nil { + break + } + if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { + break + } + } + if debugTransport { + t.printPacket(p, false) + } + + return p, err +} + +func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { + packet, err := s.packetCipher.readPacket(s.seqNum, r) + s.seqNum++ + if err == nil && len(packet) == 0 { + err = errors.New("ssh: zero length packet") + } + + if len(packet) > 0 { + switch packet[0] { + case msgNewKeys: + select { + case cipher := <-s.pendingKeyChange: + s.packetCipher = cipher + default: + return nil, errors.New("ssh: got bogus newkeys message.") + } + + case msgDisconnect: + // Transform a disconnect message into an + // error. Since this is lowest level at which + // we interpret message types, doing it here + // ensures that we don't have to handle it + // elsewhere. + var msg disconnectMsg + if err := Unmarshal(packet, &msg); err != nil { + return nil, err + } + return nil, &msg + } + } + + // The packet may point to an internal buffer, so copy the + // packet out here. + fresh := make([]byte, len(packet)) + copy(fresh, packet) + + return fresh, err +} + +func (t *transport) writePacket(packet []byte) error { + if debugTransport { + t.printPacket(packet, true) + } + return t.writer.writePacket(t.bufWriter, t.rand, packet) +} + +func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { + changeKeys := len(packet) > 0 && packet[0] == msgNewKeys + + err := s.packetCipher.writePacket(s.seqNum, w, rand, packet) + if err != nil { + return err + } + if err = w.Flush(); err != nil { + return err + } + s.seqNum++ + if changeKeys { + select { + case cipher := <-s.pendingKeyChange: + s.packetCipher = cipher + default: + panic("ssh: no key material for msgNewKeys") + } + } + return err +} + +func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport { + t := &transport{ + bufReader: bufio.NewReader(rwc), + bufWriter: bufio.NewWriter(rwc), + rand: rand, + reader: connectionState{ + packetCipher: &streamPacketCipher{cipher: noneCipher{}}, + pendingKeyChange: make(chan packetCipher, 1), + }, + writer: connectionState{ + packetCipher: &streamPacketCipher{cipher: noneCipher{}}, + pendingKeyChange: make(chan packetCipher, 1), + }, + Closer: rwc, + } + t.isClient = isClient + + if isClient { + t.reader.dir = serverKeys + t.writer.dir = clientKeys + } else { + t.reader.dir = clientKeys + t.writer.dir = serverKeys + } + + return t +} + +type direction struct { + ivTag []byte + keyTag []byte + macKeyTag []byte +} + +var ( + serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} + clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} +) + +// generateKeys generates key material for IV, MAC and encryption. +func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) { + cipherMode := cipherModes[algs.Cipher] + macMode := macModes[algs.MAC] + + iv = make([]byte, cipherMode.ivSize) + key = make([]byte, cipherMode.keySize) + macKey = make([]byte, macMode.keySize) + + generateKeyMaterial(iv, d.ivTag, kex) + generateKeyMaterial(key, d.keyTag, kex) + generateKeyMaterial(macKey, d.macKeyTag, kex) + return +} + +// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as +// described in RFC 4253, section 6.4. direction should either be serverKeys +// (to setup server->client keys) or clientKeys (for client->server keys). +func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { + iv, key, macKey := generateKeys(d, algs, kex) + + if algs.Cipher == gcmCipherID { + return newGCMCipher(iv, key, macKey) + } + + if algs.Cipher == aes128cbcID { + return newAESCBCCipher(iv, key, macKey, algs) + } + + if algs.Cipher == tripledescbcID { + return newTripleDESCBCCipher(iv, key, macKey, algs) + } + + c := &streamPacketCipher{ + mac: macModes[algs.MAC].new(macKey), + etm: macModes[algs.MAC].etm, + } + c.macResult = make([]byte, c.mac.Size()) + + var err error + c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv) + if err != nil { + return nil, err + } + + return c, nil +} + +// generateKeyMaterial fills out with key material generated from tag, K, H +// and sessionId, as specified in RFC 4253, section 7.2. +func generateKeyMaterial(out, tag []byte, r *kexResult) { + var digestsSoFar []byte + + h := r.Hash.New() + for len(out) > 0 { + h.Reset() + h.Write(r.K) + h.Write(r.H) + + if len(digestsSoFar) == 0 { + h.Write(tag) + h.Write(r.SessionID) + } else { + h.Write(digestsSoFar) + } + + digest := h.Sum(nil) + n := copy(out, digest) + out = out[n:] + if len(out) > 0 { + digestsSoFar = append(digestsSoFar, digest...) + } + } +} + +const packageVersion = "SSH-2.0-Go" + +// Sends and receives a version line. The versionLine string should +// be US ASCII, start with "SSH-2.0-", and should not include a +// newline. exchangeVersions returns the other side's version line. +func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) { + // Contrary to the RFC, we do not ignore lines that don't + // start with "SSH-2.0-" to make the library usable with + // nonconforming servers. + for _, c := range versionLine { + // The spec disallows non US-ASCII chars, and + // specifically forbids null chars. + if c < 32 { + return nil, errors.New("ssh: junk character in version line") + } + } + if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil { + return + } + + them, err = readVersion(rw) + return them, err +} + +// maxVersionStringBytes is the maximum number of bytes that we'll +// accept as a version string. RFC 4253 section 4.2 limits this at 255 +// chars +const maxVersionStringBytes = 255 + +// Read version string as specified by RFC 4253, section 4.2. +func readVersion(r io.Reader) ([]byte, error) { + versionString := make([]byte, 0, 64) + var ok bool + var buf [1]byte + + for len(versionString) < maxVersionStringBytes { + _, err := io.ReadFull(r, buf[:]) + if err != nil { + return nil, err + } + // The RFC says that the version should be terminated with \r\n + // but several SSH servers actually only send a \n. + if buf[0] == '\n' { + ok = true + break + } + + // non ASCII chars are disallowed, but we are lenient, + // since Go doesn't use null-terminated strings. + + // The RFC allows a comment after a space, however, + // all of it (version and comments) goes into the + // session hash. + versionString = append(versionString, buf[0]) + } + + if !ok { + return nil, errors.New("ssh: overflow reading version string") + } + + // There might be a '\r' on the end which we should remove. + if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { + versionString = versionString[:len(versionString)-1] + } + return versionString, nil +} diff --git a/build/_vendor/src/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go index 134654cf7..f143ed6a1 100644 --- a/build/_vendor/src/golang.org/x/net/context/context.go +++ b/vendor/golang.org/x/net/context/context.go @@ -7,7 +7,7 @@ // and between processes. // // Incoming requests to a server should create a Context, and outgoing calls to -// servers should accept a Context. The chain of function calls between must +// servers should accept a Context. The chain of function calls between must // propagate the Context, optionally replacing it with a modified copy created // using WithDeadline, WithTimeout, WithCancel, or WithValue. // @@ -16,14 +16,14 @@ // propagation: // // Do not store Contexts inside a struct type; instead, pass a Context -// explicitly to each function that needs it. The Context should be the first +// explicitly to each function that needs it. The Context should be the first // parameter, typically named ctx: // // func DoSomething(ctx context.Context, arg Arg) error { // // ... use ctx ... // } // -// Do not pass a nil Context, even if a function permits it. Pass context.TODO +// Do not pass a nil Context, even if a function permits it. Pass context.TODO // if you are unsure about which Context to use. // // Use context Values only for request-scoped data that transits processes and @@ -44,13 +44,13 @@ import "time" // Context's methods may be called by multiple goroutines simultaneously. type Context interface { // Deadline returns the time when work done on behalf of this context - // should be canceled. Deadline returns ok==false when no deadline is - // set. Successive calls to Deadline return the same results. + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. Deadline() (deadline time.Time, ok bool) // Done returns a channel that's closed when work done on behalf of this - // context should be canceled. Done may return nil if this context can - // never be canceled. Successive calls to Done return the same value. + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. // // WithCancel arranges for Done to be closed when cancel is called; // WithDeadline arranges for Done to be closed when the deadline @@ -79,24 +79,24 @@ type Context interface { // a Done channel for cancelation. Done() <-chan struct{} - // Err returns a non-nil error value after Done is closed. Err returns + // Err returns a non-nil error value after Done is closed. Err returns // Canceled if the context was canceled or DeadlineExceeded if the - // context's deadline passed. No other values for Err are defined. + // context's deadline passed. No other values for Err are defined. // After Done is closed, successive calls to Err return the same value. Err() error // Value returns the value associated with this context for key, or nil - // if no value is associated with key. Successive calls to Value with + // if no value is associated with key. Successive calls to Value with // the same key returns the same result. // // Use context values only for request-scoped data that transits // processes and API boundaries, not for passing optional parameters to // functions. // - // A key identifies a specific value in a Context. Functions that wish + // A key identifies a specific value in a Context. Functions that wish // to store values in Context typically allocate a key in a global // variable then use that key as the argument to context.WithValue and - // Context.Value. A key can be any type that supports equality; + // Context.Value. A key can be any type that supports equality; // packages should define keys as an unexported type to avoid // collisions. // @@ -115,7 +115,7 @@ type Context interface { // // This prevents collisions with keys defined in other packages. // type key int // - // // userKey is the key for user.User values in Contexts. It is + // // userKey is the key for user.User values in Contexts. It is // // unexported; clients use user.NewContext and user.FromContext // // instead of using this key directly. // var userKey key = 0 @@ -134,14 +134,14 @@ type Context interface { } // Background returns a non-nil, empty Context. It is never canceled, has no -// values, and has no deadline. It is typically used by the main function, +// values, and has no deadline. It is typically used by the main function, // initialization, and tests, and as the top-level Context for incoming // requests. func Background() Context { return background } -// TODO returns a non-nil, empty Context. Code should use context.TODO when +// TODO returns a non-nil, empty Context. Code should use context.TODO when // it's unclear which Context to use or it is not yet available (because the // surrounding function has not yet been extended to accept a Context // parameter). TODO is recognized by static analysis tools that determine diff --git a/build/_vendor/src/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go index f8cda19ad..d20f52b7d 100644 --- a/build/_vendor/src/golang.org/x/net/context/go17.go +++ b/vendor/golang.org/x/net/context/go17.go @@ -35,8 +35,8 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { } // WithDeadline returns a copy of the parent context with the deadline adjusted -// to be no later than d. If the parent's deadline is already earlier than d, -// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned // context's Done channel is closed when the deadline expires, when the returned // cancel function is called, or when the parent context's Done channel is // closed, whichever happens first. diff --git a/build/_vendor/src/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go index 5a30acabd..0f35592df 100644 --- a/build/_vendor/src/golang.org/x/net/context/pre_go17.go +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -13,7 +13,7 @@ import ( "time" ) -// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// An emptyCtx is never canceled, has no values, and has no deadline. It is not // struct{}, since vars of this type must have distinct addresses. type emptyCtx int @@ -104,7 +104,7 @@ func propagateCancel(parent Context, child canceler) { } // parentCancelCtx follows a chain of parent references until it finds a -// *cancelCtx. This function understands how each of the concrete types in this +// *cancelCtx. This function understands how each of the concrete types in this // package represents its parent. func parentCancelCtx(parent Context) (*cancelCtx, bool) { for { @@ -134,14 +134,14 @@ func removeChild(parent Context, child canceler) { p.mu.Unlock() } -// A canceler is a context type that can be canceled directly. The +// A canceler is a context type that can be canceled directly. The // implementations are *cancelCtx and *timerCtx. type canceler interface { cancel(removeFromParent bool, err error) Done() <-chan struct{} } -// A cancelCtx can be canceled. When canceled, it also cancels any children +// A cancelCtx can be canceled. When canceled, it also cancels any children // that implement canceler. type cancelCtx struct { Context @@ -193,8 +193,8 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) { } // WithDeadline returns a copy of the parent context with the deadline adjusted -// to be no later than d. If the parent's deadline is already earlier than d, -// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned // context's Done channel is closed when the deadline expires, when the returned // cancel function is called, or when the parent context's Done channel is // closed, whichever happens first. @@ -226,8 +226,8 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { return c, func() { c.cancel(true, Canceled) } } -// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to -// implement Done and Err. It implements cancel by stopping its timer then +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then // delegating to cancelCtx.cancel. type timerCtx struct { *cancelCtx @@ -281,7 +281,7 @@ func WithValue(parent Context, key interface{}, val interface{}) Context { return &valueCtx{parent, key, val} } -// A valueCtx carries a key-value pair. It implements Value for that key and +// A valueCtx carries a key-value pair. It implements Value for that key and // delegates all other calls to the embedded Context. type valueCtx struct { Context diff --git a/vendor/vendor.json b/vendor/vendor.json index 58bbd82ff..d79c80a67 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1,14 +1,48 @@ { "comment": "", - "ignore": "test golang.org/x/net/context", + "ignore": "test", "package": [ { - "checksumSHA1": "M30X+Wqn7AnUr1numUOkQRI7ET0=", - "path": "github.com/Azure/azure-sdk-for-go/storage", - "revision": "e01c89c9c29b97413ae6ebe89a294d814ec7ca8b", - "revisionTime": "2016-10-28T18:35:05Z", - "version": "v6", - "versionExact": "v6.0.0-beta" + "checksumSHA1": "b5bkSc2hlmUV7PlLY6JlLwiJpiE=", + "path": "bazil.org/fuse", + "revision": "371fbbdaa8987b715bdd21d6adc4c9b20155f748", + "revisionTime": "2016-08-11T21:22:31Z" + }, + { + "checksumSHA1": "389JFJTJADMtZkTIfdSnsmHVOUs=", + "path": "bazil.org/fuse/fs", + "revision": "371fbbdaa8987b715bdd21d6adc4c9b20155f748", + "revisionTime": "2016-08-11T21:22:31Z" + }, + { + "checksumSHA1": "NPgkh9UWMsaTtsAAs3kPrclHT9Y=", + "path": "bazil.org/fuse/fuseutil", + "revision": "371fbbdaa8987b715bdd21d6adc4c9b20155f748", + "revisionTime": "2016-08-11T21:22:31Z" + }, + { + "checksumSHA1": "24EW3PGLGVSy/5joAOZdHzN/S2E=", + "path": "github.com/Azure/azure-storage-go", + "revision": "12ccaadb081cdd217702067d28da9a7ff4305239", + "revisionTime": "2017-03-02T22:15:08Z" + }, + { + "checksumSHA1": "+sxS3YEvxf1VxlRskIdXFRWq9rY=", + "path": "github.com/Azure/go-autorest/autorest", + "revision": "ec5f4903f77ed9927ac95b19ab8e44ada64c1356", + "revisionTime": "2017-02-15T19:58:14Z" + }, + { + "checksumSHA1": "ghrnc4vZv6q8zzeakZnrS8CGFhE=", + "path": "github.com/Azure/go-autorest/autorest/azure", + "revision": "ec5f4903f77ed9927ac95b19ab8e44ada64c1356", + "revisionTime": "2017-02-15T19:58:14Z" + }, + { + "checksumSHA1": "q9Qz8PAxK5FTOZwgYKe5Lj38u4c=", + "path": "github.com/Azure/go-autorest/autorest/date", + "revision": "ec5f4903f77ed9927ac95b19ab8e44ada64c1356", + "revisionTime": "2017-02-15T19:58:14Z" }, { "checksumSHA1": "USkefO0g1U9mr+8hagv3fpSkrxg=", @@ -35,6 +69,12 @@ "revisionTime": "2016-10-29T20:57:26Z" }, { + "checksumSHA1": "2Fy1Y6Z3lRRX1891WF/+HT4XS2I=", + "path": "github.com/dgrijalva/jwt-go", + "revision": "2268707a8f0843315e2004ee4f1d021dc08baedf", + "revisionTime": "2017-02-01T22:58:49Z" + }, + { "checksumSHA1": "zYnPsNAVm1/ViwCkN++dX2JQhBo=", "path": "github.com/edsrzf/mmap-go", "revision": "935e0e8a636ca4ba70b713f3e38a19e1b77739e8", @@ -162,12 +202,30 @@ "revisionTime": "2015-03-14T17:03:34Z" }, { + "checksumSHA1": "2gmvVTDCks8cPhpmyDlvm0sbrXE=", + "path": "github.com/naoina/toml", + "revision": "ac014c6b6502388d89a85552b7208b8da7cfe104", + "revisionTime": "2017-04-10T21:57:17Z" + }, + { + "checksumSHA1": "xZBlSMT5o/A+EDOro6KbfHZwSNc=", + "path": "github.com/naoina/toml/ast", + "revision": "eb52202f758b98ac5b1a8eb26f36455205d688f0", + "revisionTime": "2017-04-03T15:03:10Z" + }, + { "checksumSHA1": "R1h9XHH3dTmLq7yKL9/uW0xFwfs=", "path": "github.com/nsf/termbox-go", "revision": "3540b76b9c77679aeffd0a47e00243fb0ce47133", "revisionTime": "2017-02-11T01:27:00Z" }, { + "checksumSHA1": "h+oCMj21PiQfIdBog0eyUtF1djs=", + "path": "github.com/olekukonko/tablewriter", + "revision": "febf2d34b54a69ce7530036c7503b1c9fbfdf0bb", + "revisionTime": "2017-01-28T05:05:32Z" + }, + { "checksumSHA1": "Se195FlZ160eaEk/uVx4KdTPSxU=", "path": "github.com/pborman/uuid", "revision": "1b00554d822231195d1babd97ff4a781231955c9", @@ -330,6 +388,24 @@ "revisionTime": "2017-02-08T20:51:15Z" }, { + "checksumSHA1": "C1KKOxFoW7/W/NFNpiXK+boguNo=", + "path": "golang.org/x/crypto/curve25519", + "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7", + "revisionTime": "2017-03-17T13:29:17Z" + }, + { + "checksumSHA1": "wGb//LjBPNxYHqk+dcLo7BjPXK8=", + "path": "golang.org/x/crypto/ed25519", + "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7", + "revisionTime": "2017-03-17T13:29:17Z" + }, + { + "checksumSHA1": "LXFcVx8I587SnWmKycSDEq9yvK8=", + "path": "golang.org/x/crypto/ed25519/internal/edwards25519", + "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7", + "revisionTime": "2017-03-17T13:29:17Z" + }, + { "checksumSHA1": "IIhFTrLlmlc6lEFSitqi4aw2lw0=", "path": "golang.org/x/crypto/openpgp", "revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8", @@ -384,6 +460,24 @@ "revisionTime": "2017-02-08T20:51:15Z" }, { + "checksumSHA1": "fsrFs762jlaILyqqQImS1GfvIvw=", + "path": "golang.org/x/crypto/ssh", + "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7", + "revisionTime": "2017-03-17T13:29:17Z" + }, + { + "checksumSHA1": "xiderUuvye8Kpn7yX3niiJg32bE=", + "path": "golang.org/x/crypto/ssh/terminal", + "revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7", + "revisionTime": "2017-03-17T13:29:17Z" + }, + { + "checksumSHA1": "Y+HGqEkYM15ir+J93MEaHdyFy0c=", + "path": "golang.org/x/net/context", + "revision": "a6577fac2d73be281a500b310739095313165611", + "revisionTime": "2017-03-08T20:54:49Z" + }, + { "checksumSHA1": "vqc3a+oTUGX8PmD0TS+qQ7gmN8I=", "path": "golang.org/x/net/html", "revision": "b4690f45fa1cafc47b1c280c2e75116efe40cc13", diff --git a/whisper/mailserver/mailserver.go b/whisper/mailserver/mailserver.go index 6533c56c2..d705c622f 100644 --- a/whisper/mailserver/mailserver.go +++ b/whisper/mailserver/mailserver.go @@ -31,8 +31,6 @@ import ( "github.com/syndtr/goleveldb/leveldb/util" ) -const MailServerKeyName = "958e04ab302fb36ad2616a352cbac79d" - type WMailServer struct { db *leveldb.DB w *whisper.Whisper @@ -75,11 +73,14 @@ func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, p s.w = shh s.pow = pow - err = s.w.AddSymKey(MailServerKeyName, []byte(password)) + MailServerKeyID, err := s.w.AddSymKeyFromPassword(password) if err != nil { utils.Fatalf("Failed to create symmetric key for MailServer: %s", err) } - s.key = s.w.GetSymKey(MailServerKeyName) + s.key, err = s.w.GetSymKey(MailServerKeyID) + if err != nil { + utils.Fatalf("Failed to save symmetric key for MailServer") + } } func (s *WMailServer) Close() { diff --git a/whisper/mailserver/server_test.go b/whisper/mailserver/server_test.go index 8b58a826f..ffdff3191 100644 --- a/whisper/mailserver/server_test.go +++ b/whisper/mailserver/server_test.go @@ -30,8 +30,8 @@ import ( ) const powRequirement = 0.00001 -const keyName = "6d604bac5401ce9a6b995f1b45a4ab" +var keyID string var shh *whisper.Whisper var seed = time.Now().Unix() @@ -90,7 +90,7 @@ func TestMailServer(t *testing.T) { server.Init(shh, dir, password, powRequirement) defer server.Close() - err = shh.AddSymKey(keyName, []byte(password)) + keyID, err = shh.AddSymKeyFromPassword(password) if err != nil { t.Fatalf("Failed to create symmetric key for mail request: %s", err) } @@ -102,7 +102,14 @@ func TestMailServer(t *testing.T) { } func deliverTest(t *testing.T, server *WMailServer, env *whisper.Envelope) { - testPeerID := shh.NewIdentity() + id, err := shh.NewKeyPair() + if err != nil { + t.Fatalf("failed to generate new key pair with seed %d: %s.", seed, err) + } + testPeerID, err := shh.GetPrivateKey(id) + if err != nil { + t.Fatalf("failed to retrieve new key pair with seed %d: %s.", seed, err) + } birth := env.Expiry - env.TTL p := &ServerTestParams{ topic: env.Topic, @@ -167,8 +174,13 @@ func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope { binary.BigEndian.PutUint32(data[4:], p.upp) copy(data[8:], p.topic[:]) + key, err := shh.GetSymKey(keyID) + if err != nil { + t.Fatalf("failed to retrieve sym key with seed %d: %s.", seed, err) + } + params := &whisper.MessageParams{ - KeySym: shh.GetSymKey(keyName), + KeySym: key, Topic: p.topic, Payload: data, PoW: powRequirement * 2, diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go index 9b43f7b70..579efba9e 100644 --- a/whisper/whisperv5/api.go +++ b/whisper/whisperv5/api.go @@ -20,15 +20,14 @@ import ( "encoding/json" "errors" "fmt" - mathrand "math/rand" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/discover" ) -var whisperOffLineErr = errors.New("whisper is offline") +var whisperOfflineErr = errors.New("whisper is offline") // PublicWhisperAPI provides the whisper RPC service. type PublicWhisperAPI struct { @@ -43,7 +42,7 @@ func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { // Start starts the Whisper worker threads. func (api *PublicWhisperAPI) Start() error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } return api.whisper.Start(nil) } @@ -51,7 +50,7 @@ func (api *PublicWhisperAPI) Start() error { // Stop stops the Whisper worker threads. func (api *PublicWhisperAPI) Stop() error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } return api.whisper.Stop() } @@ -59,179 +58,219 @@ func (api *PublicWhisperAPI) Stop() error { // Version returns the Whisper version this node offers. func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) { if api.whisper == nil { - return 0, whisperOffLineErr + return 0, whisperOfflineErr } return hexutil.Uint(api.whisper.Version()), nil } -// Stats returns the Whisper statistics for diagnostics. -func (api *PublicWhisperAPI) Stats() (string, error) { +// Info returns the Whisper statistics for diagnostics. +func (api *PublicWhisperAPI) Info() (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } return api.whisper.Stats(), nil } -// MarkPeerTrusted marks specific peer trusted, which will allow it +// SetMaxMessageLength sets the maximal message length allowed by this node +func (api *PublicWhisperAPI) SetMaxMessageLength(val int) error { + if api.whisper == nil { + return whisperOfflineErr + } + return api.whisper.SetMaxMessageLength(val) +} + +// SetMinimumPoW sets the minimal PoW required by this node +func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error { + if api.whisper == nil { + return whisperOfflineErr + } + return api.whisper.SetMinimumPoW(val) +} + +// AllowP2PMessagesFromPeer marks specific peer trusted, which will allow it // to send historic (expired) messages. -func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error { +func (api *PublicWhisperAPI) AllowP2PMessagesFromPeer(enode string) error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } - return api.whisper.MarkPeerTrusted(peerID) + n, err := discover.ParseNode(enode) + if err != nil { + return errors.New("failed to parse enode of trusted peer: " + err.Error()) + } + return api.whisper.AllowP2PMessagesFromPeer(n.ID[:]) } -// RequestHistoricMessages requests the peer to deliver the old (expired) messages. -// data contains parameters (time frame, payment details, etc.), required -// by the remote email-like server. Whisper is not aware about the data format, -// it will just forward the raw data to the server. -//func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error { -// if api.whisper == nil { -// return whisperOffLineErr -// } -// return api.whisper.RequestHistoricMessages(peerID, data) -//} - -// HasIdentity checks if the whisper node is configured with the private key +// HasKeyPair checks if the whisper node is configured with the private key // of the specified public pair. -func (api *PublicWhisperAPI) HasIdentity(identity string) (bool, error) { +func (api *PublicWhisperAPI) HasKeyPair(id string) (bool, error) { if api.whisper == nil { - return false, whisperOffLineErr + return false, whisperOfflineErr } - return api.whisper.HasIdentity(identity), nil + return api.whisper.HasKeyPair(id), nil } -// DeleteIdentity deletes the specifies key if it exists. -func (api *PublicWhisperAPI) DeleteIdentity(identity string) error { +// DeleteKeyPair deletes the specifies key if it exists. +func (api *PublicWhisperAPI) DeleteKeyPair(id string) (bool, error) { if api.whisper == nil { - return whisperOffLineErr + return false, whisperOfflineErr } - api.whisper.DeleteIdentity(identity) - return nil + return api.whisper.DeleteKeyPair(id), nil } -// NewIdentity generates a new cryptographic identity for the client, and injects +// NewKeyPair generates a new cryptographic identity for the client, and injects // it into the known identities for message decryption. -func (api *PublicWhisperAPI) NewIdentity() (string, error) { +func (api *PublicWhisperAPI) NewKeyPair() (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } - identity := api.whisper.NewIdentity() - return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil + return api.whisper.NewKeyPair() } -// GenerateSymKey generates a random symmetric key and stores it under -// the 'name' id. Will be used in the future for session key exchange. -func (api *PublicWhisperAPI) GenerateSymKey(name string) error { +// GetPublicKey returns the public key for identity id +func (api *PublicWhisperAPI) GetPublicKey(id string) (hexutil.Bytes, error) { if api.whisper == nil { - return whisperOffLineErr + return nil, whisperOfflineErr + } + key, err := api.whisper.GetPrivateKey(id) + if err != nil { + return nil, err } - return api.whisper.GenerateSymKey(name) + return crypto.FromECDSAPub(&key.PublicKey), nil } -// AddSymKey stores the key under the 'name' id. -func (api *PublicWhisperAPI) AddSymKey(name string, key hexutil.Bytes) error { +// GetPrivateKey returns the private key for identity id +func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) { if api.whisper == nil { - return whisperOffLineErr + return "", whisperOfflineErr } - return api.whisper.AddSymKey(name, key) + key, err := api.whisper.GetPrivateKey(id) + if err != nil { + return "", err + } + return common.ToHex(crypto.FromECDSA(key)), nil } -// HasSymKey returns true if there is a key associated with the name string. -// Otherwise returns false. -func (api *PublicWhisperAPI) HasSymKey(name string) (bool, error) { +// GenerateSymmetricKey generates a random symmetric key and stores it under id, +// which is then returned. Will be used in the future for session key exchange. +func (api *PublicWhisperAPI) GenerateSymmetricKey() (string, error) { if api.whisper == nil { - return false, whisperOffLineErr + return "", whisperOfflineErr } - res := api.whisper.HasSymKey(name) - return res, nil + return api.whisper.GenerateSymKey() } -// DeleteSymKey deletes the key associated with the name string if it exists. -func (api *PublicWhisperAPI) DeleteSymKey(name string) error { +// AddSymmetricKeyDirect stores the key, and returns its id. +func (api *PublicWhisperAPI) AddSymmetricKeyDirect(key hexutil.Bytes) (string, error) { if api.whisper == nil { - return whisperOffLineErr + return "", whisperOfflineErr } - api.whisper.DeleteSymKey(name) - return nil + return api.whisper.AddSymKeyDirect(key) } -// NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages. -// Returns the ID of the newly created Filter. -func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) { +// AddSymmetricKeyFromPassword generates the key from password, stores it, and returns its id. +func (api *PublicWhisperAPI) AddSymmetricKeyFromPassword(password string) (string, error) { if api.whisper == nil { - return "", whisperOffLineErr + return "", whisperOfflineErr } + return api.whisper.AddSymKeyFromPassword(password) +} - filter := Filter{ - Src: crypto.ToECDSAPub(common.FromHex(args.From)), - KeySym: api.whisper.GetSymKey(args.KeyName), - PoW: args.PoW, - Messages: make(map[common.Hash]*ReceivedMessage), - AcceptP2P: args.AcceptP2P, - } - if len(filter.KeySym) > 0 { - filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) +// HasSymmetricKey returns true if there is a key associated with the given id. +// Otherwise returns false. +func (api *PublicWhisperAPI) HasSymmetricKey(id string) (bool, error) { + if api.whisper == nil { + return false, whisperOfflineErr } - filter.Topics = append(filter.Topics, args.Topics...) + res := api.whisper.HasSymKey(id) + return res, nil +} - if len(args.Topics) == 0 && len(args.KeyName) != 0 { - info := "NewFilter: at least one topic must be specified" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) +// GetSymmetricKey returns the symmetric key associated with the given id. +func (api *PublicWhisperAPI) GetSymmetricKey(name string) (hexutil.Bytes, error) { + if api.whisper == nil { + return nil, whisperOfflineErr + } + b, err := api.whisper.GetSymKey(name) + if err != nil { + return nil, err } + return b, nil +} - if len(args.KeyName) != 0 && len(filter.KeySym) == 0 { - info := "NewFilter: key was not found by name: " + args.KeyName - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) +// DeleteSymmetricKey deletes the key associated with the name string if it exists. +func (api *PublicWhisperAPI) DeleteSymmetricKey(name string) (bool, error) { + if api.whisper == nil { + return false, whisperOfflineErr } + res := api.whisper.DeleteSymKey(name) + return res, nil +} - if len(args.To) == 0 && len(filter.KeySym) == 0 { - info := "NewFilter: filter must contain either symmetric or asymmetric key" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) +// Subscribe creates and registers a new filter to watch for inbound whisper messages. +// Returns the ID of the newly created filter. +func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) { + if api.whisper == nil { + return "", whisperOfflineErr } - if len(args.To) != 0 && len(filter.KeySym) != 0 { - info := "NewFilter: filter must not contain both symmetric and asymmetric key" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) + filter := Filter{ + Src: crypto.ToECDSAPub(common.FromHex(args.SignedWith)), + PoW: args.MinPoW, + Messages: make(map[common.Hash]*ReceivedMessage), + AllowP2P: args.AllowP2P, } - if len(args.To) > 0 { - dst := crypto.ToECDSAPub(common.FromHex(args.To)) - if !ValidatePublicKey(dst) { - info := "NewFilter: Invalid 'To' address" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) - } - filter.KeyAsym = api.whisper.GetIdentity(string(args.To)) - if filter.KeyAsym == nil { - info := "NewFilter: non-existent identity provided" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) + var err error + for i, bt := range args.Topics { + if len(bt) == 0 || len(bt) > 4 { + return "", errors.New(fmt.Sprintf("subscribe: topic %d has wrong size: %d", i, len(bt))) } + filter.Topics = append(filter.Topics, bt) } - if len(args.From) > 0 { + if err = ValidateKeyID(args.Key); err != nil { + return "", errors.New("subscribe: " + err.Error()) + } + + if len(args.SignedWith) > 0 { if !ValidatePublicKey(filter.Src) { - info := "NewFilter: Invalid 'From' address" - log.Error(fmt.Sprintf(info)) - return "", errors.New(info) + return "", errors.New("subscribe: invalid 'SignedWith' field") + } + } + + if args.Symmetric { + if len(args.Topics) == 0 { + return "", errors.New("subscribe: at least one topic must be specified with symmetric encryption") + } + symKey, err := api.whisper.GetSymKey(args.Key) + if err != nil { + return "", errors.New("subscribe: invalid key ID") + } + if !validateSymmetricKey(symKey) { + return "", errors.New("subscribe: retrieved key is invalid") + } + filter.KeySym = symKey + filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) + } else { + filter.KeyAsym, err = api.whisper.GetPrivateKey(args.Key) + if err != nil { + return "", errors.New("subscribe: invalid key ID") + } + if filter.KeyAsym == nil { + return "", errors.New("subscribe: non-existent identity provided") } } - return api.whisper.Watch(&filter) + return api.whisper.Subscribe(&filter) } -// UninstallFilter disables and removes an existing filter. -func (api *PublicWhisperAPI) UninstallFilter(filterId string) { - api.whisper.Unwatch(filterId) +// Unsubscribe disables and removes an existing filter. +func (api *PublicWhisperAPI) Unsubscribe(id string) { + api.whisper.Unsubscribe(id) } -// GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval. -func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage { +// GetSubscriptionMessages retrieves all the new messages matched by a filter since the last retrieval. +func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*WhisperMessage { f := api.whisper.GetFilter(filterId) if f != nil { newMail := f.Retrieve() @@ -240,7 +279,8 @@ func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage return toWhisperMessages(nil) } -// GetMessages retrieves all the known messages that match a specific filter. +// GetMessages retrieves all the floating messages that match a specific filter. +// It is likely to be called once per session, right after Subscribe call. func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage { all := api.whisper.Messages(filterId) return toWhisperMessages(all) @@ -258,139 +298,107 @@ func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage { // Post creates a whisper message and injects it into the network for distribution. func (api *PublicWhisperAPI) Post(args PostArgs) error { if api.whisper == nil { - return whisperOffLineErr + return whisperOfflineErr } + var err error params := MessageParams{ TTL: args.TTL, - Dst: crypto.ToECDSAPub(common.FromHex(args.To)), - KeySym: api.whisper.GetSymKey(args.KeyName), - Topic: args.Topic, + WorkTime: args.PowTime, + PoW: args.PowTarget, Payload: args.Payload, Padding: args.Padding, - WorkTime: args.WorkTime, - PoW: args.PoW, } - if len(args.From) > 0 { - pub := crypto.ToECDSAPub(common.FromHex(args.From)) - if !ValidatePublicKey(pub) { - info := "Post: Invalid 'From' address" - log.Error(fmt.Sprintf(info)) - return errors.New(info) + if len(args.Key) == 0 { + return errors.New("post: key is missing") + } + + if len(args.SignWith) > 0 { + params.Src, err = api.whisper.GetPrivateKey(args.SignWith) + if err != nil { + return err } - params.Src = api.whisper.GetIdentity(string(args.From)) if params.Src == nil { - info := "Post: non-existent identity provided" - log.Error(fmt.Sprintf(info)) - return errors.New(info) + return errors.New("post: empty identity") } } - filter := api.whisper.GetFilter(args.FilterID) - if filter == nil && len(args.FilterID) > 0 { - info := fmt.Sprintf("Post: wrong filter id %s", args.FilterID) - log.Error(fmt.Sprintf(info)) - return errors.New(info) + if len(args.Topic) == TopicLength { + params.Topic = BytesToTopic(args.Topic) + } else if len(args.Topic) != 0 { + return errors.New(fmt.Sprintf("post: wrong topic size %d", len(args.Topic))) } - if filter != nil { - // get the missing fields from the filter - if params.KeySym == nil && filter.KeySym != nil { - params.KeySym = filter.KeySym + if args.Type == "sym" { + if err = ValidateKeyID(args.Key); err != nil { + return err } - if params.Src == nil && filter.Src != nil { - params.Src = filter.KeyAsym + params.KeySym, err = api.whisper.GetSymKey(args.Key) + if err != nil { + return err } - if (params.Topic == TopicType{}) { - sz := len(filter.Topics) - if sz < 1 { - info := fmt.Sprintf("Post: no topics in filter # %s", args.FilterID) - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } else if sz == 1 { - params.Topic = filter.Topics[0] - } else { - // choose randomly - rnd := mathrand.Intn(sz) - params.Topic = filter.Topics[rnd] - } + if !validateSymmetricKey(params.KeySym) { + return errors.New("post: key for symmetric encryption is invalid") } - } - - // validate - if len(args.KeyName) != 0 && len(params.KeySym) == 0 { - info := "Post: key was not found by name: " + args.KeyName - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } - - if len(args.To) == 0 && len(params.KeySym) == 0 { - info := "Post: message must be encrypted either symmetrically or asymmetrically" - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } - - if len(args.To) != 0 && len(params.KeySym) != 0 { - info := "Post: ambigous encryption method requested" - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } - - if len(args.To) > 0 { + if len(params.Topic) == 0 { + return errors.New("post: topic is missing for symmetric encryption") + } + } else if args.Type == "asym" { + params.Dst = crypto.ToECDSAPub(common.FromHex(args.Key)) if !ValidatePublicKey(params.Dst) { - info := "Post: Invalid 'To' address" - log.Error(fmt.Sprintf(info)) - return errors.New(info) + return errors.New("post: public key for asymmetric encryption is invalid") } + } else { + return errors.New("post: wrong type (sym/asym)") } // encrypt and send message := NewSentMessage(¶ms) + if message == nil { + return errors.New("post: failed create new message, probably due to failed rand function (OS level)") + } envelope, err := message.Wrap(¶ms) if err != nil { - log.Error(fmt.Sprintf(err.Error())) return err } - if len(envelope.Data) > MaxMessageLength { - info := "Post: message is too big" - log.Error(fmt.Sprintf(info)) - return errors.New(info) - } - if (envelope.Topic == TopicType{} && envelope.IsSymmetric()) { - info := "Post: topic is missing for symmetric encryption" - log.Error(fmt.Sprintf(info)) - return errors.New(info) + if envelope.size() > api.whisper.maxMsgLength { + return errors.New("post: message is too big") } - if args.PeerID != nil { - return api.whisper.SendP2PMessage(args.PeerID, envelope) + if len(args.TargetPeer) != 0 { + n, err := discover.ParseNode(args.TargetPeer) + if err != nil { + return errors.New("post: failed to parse enode of target peer: " + err.Error()) + } + return api.whisper.SendP2PMessage(n.ID[:], envelope) + } else if args.PowTarget < api.whisper.minPoW { + return errors.New("post: target PoW is less than minimum PoW, the message can not be sent") } return api.whisper.Send(envelope) } type PostArgs struct { - TTL uint32 `json:"ttl"` - From string `json:"from"` - To string `json:"to"` - KeyName string `json:"keyname"` - Topic TopicType `json:"topic"` - Padding hexutil.Bytes `json:"padding"` - Payload hexutil.Bytes `json:"payload"` - WorkTime uint32 `json:"worktime"` - PoW float64 `json:"pow"` - FilterID string `json:"filterID"` - PeerID hexutil.Bytes `json:"peerID"` + Type string `json:"type"` // "sym"/"asym" (symmetric or asymmetric) + TTL uint32 `json:"ttl"` // time-to-live in seconds + SignWith string `json:"signWith"` // id of the signing key + Key string `json:"key"` // id of encryption key + Topic hexutil.Bytes `json:"topic"` // topic (4 bytes) + Padding hexutil.Bytes `json:"padding"` // optional padding bytes + Payload hexutil.Bytes `json:"payload"` // payload to be encrypted + PowTime uint32 `json:"powTime"` // maximal time in seconds to be spent on PoW + PowTarget float64 `json:"powTarget"` // minimal PoW required for this message + TargetPeer string `json:"targetPeer"` // peer id (for p2p message only) } type WhisperFilterArgs struct { - To string `json:"to"` - From string `json:"from"` - KeyName string `json:"keyname"` - PoW float64 `json:"pow"` - Topics []TopicType `json:"topics"` - AcceptP2P bool `json:"p2p"` + Symmetric bool // encryption type + Key string // id of the key to be used for decryption + SignedWith string // public key of the sender to be verified + MinPoW float64 // minimal PoW requirement + Topics [][]byte // list of topics (up to 4 bytes each) to match + AllowP2P bool // indicates wheather direct p2p messages are allowed for this filter } // UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a @@ -398,22 +406,30 @@ type WhisperFilterArgs struct { func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { // Unmarshal the JSON message and sanity check var obj struct { - To string `json:"to"` - From string `json:"from"` - KeyName string `json:"keyname"` - PoW float64 `json:"pow"` - Topics []interface{} `json:"topics"` - AcceptP2P bool `json:"p2p"` + Type string `json:"type"` + Key string `json:"key"` + SignedWith string `json:"signedWith"` + MinPoW float64 `json:"minPoW"` + Topics []interface{} `json:"topics"` + AllowP2P bool `json:"allowP2P"` } if err := json.Unmarshal(b, &obj); err != nil { return err } - args.To = obj.To - args.From = obj.From - args.KeyName = obj.KeyName - args.PoW = obj.PoW - args.AcceptP2P = obj.AcceptP2P + switch obj.Type { + case "sym": + args.Symmetric = true + case "asym": + args.Symmetric = false + default: + return errors.New("wrong type (sym/asym)") + } + + args.Key = obj.Key + args.SignedWith = obj.SignedWith + args.MinPoW = obj.MinPoW + args.AllowP2P = obj.AllowP2P // Construct the topic array if obj.Topics != nil { @@ -428,13 +444,13 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { return fmt.Errorf("topic[%d] is not a string", i) } } - topicsDecoded := make([]TopicType, len(topics)) + topicsDecoded := make([][]byte, len(topics)) for j, s := range topics { x := common.FromHex(s) - if x == nil || len(x) != TopicLength { + if x == nil || len(x) > TopicLength { return fmt.Errorf("topic[%d] is invalid", j) } - topicsDecoded[j] = BytesToTopic(x) + topicsDecoded[j] = x } args.Topics = topicsDecoded } @@ -444,34 +460,34 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { // WhisperMessage is the RPC representation of a whisper message. type WhisperMessage struct { - Topic string `json:"topic"` - Payload string `json:"payload"` - Padding string `json:"padding"` - From string `json:"from"` - To string `json:"to"` - Sent uint32 `json:"sent"` - TTL uint32 `json:"ttl"` - PoW float64 `json:"pow"` - Hash string `json:"hash"` + Topic string `json:"topic"` + Payload string `json:"payload"` + Padding string `json:"padding"` + Src string `json:"signedWith"` + Dst string `json:"recipientPublicKey"` + Timestamp uint32 `json:"timestamp"` + TTL uint32 `json:"ttl"` + PoW float64 `json:"pow"` + Hash string `json:"hash"` } // NewWhisperMessage converts an internal message into an API version. func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage { msg := WhisperMessage{ - Topic: common.ToHex(message.Topic[:]), - Payload: common.ToHex(message.Payload), - Padding: common.ToHex(message.Padding), - Sent: message.Sent, - TTL: message.TTL, - PoW: message.PoW, - Hash: common.ToHex(message.EnvelopeHash.Bytes()), + Topic: common.ToHex(message.Topic[:]), + Payload: common.ToHex(message.Payload), + Padding: common.ToHex(message.Padding), + Timestamp: message.Sent, + TTL: message.TTL, + PoW: message.PoW, + Hash: common.ToHex(message.EnvelopeHash.Bytes()), } if message.Dst != nil { - msg.To = common.ToHex(crypto.FromECDSAPub(message.Dst)) + msg.Dst = common.ToHex(crypto.FromECDSAPub(message.Dst)) } if isMessageSigned(message.Raw[0]) { - msg.From = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())) + msg.Src = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey())) } return &msg } diff --git a/whisper/whisperv5/api_test.go b/whisper/whisperv5/api_test.go index ea0a2c40b..9207c6f10 100644 --- a/whisper/whisperv5/api_test.go +++ b/whisper/whisperv5/api_test.go @@ -23,6 +23,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" ) func TestBasic(t *testing.T) { @@ -42,12 +43,12 @@ func TestBasic(t *testing.T) { t.Fatalf("wrong version: %d.", ver) } - mail := api.GetFilterChanges("non-existent-id") + mail := api.GetSubscriptionMessages("non-existent-id") if len(mail) != 0 { t.Fatalf("failed GetFilterChanges: premature result") } - exist, err := api.HasIdentity(id) + exist, err := api.HasKeyPair(id) if err != nil { t.Fatalf("failed initial HasIdentity: %s.", err) } @@ -55,12 +56,15 @@ func TestBasic(t *testing.T) { t.Fatalf("failed initial HasIdentity: false positive.") } - err = api.DeleteIdentity(id) + success, err := api.DeleteKeyPair(id) if err != nil { t.Fatalf("failed DeleteIdentity: %s.", err) } + if success { + t.Fatalf("deleted non-existing identity: false positive.") + } - pub, err := api.NewIdentity() + pub, err := api.NewKeyPair() if err != nil { t.Fatalf("failed NewIdentity: %s.", err) } @@ -68,7 +72,7 @@ func TestBasic(t *testing.T) { t.Fatalf("failed NewIdentity: empty") } - exist, err = api.HasIdentity(pub) + exist, err = api.HasKeyPair(pub) if err != nil { t.Fatalf("failed HasIdentity: %s.", err) } @@ -76,12 +80,15 @@ func TestBasic(t *testing.T) { t.Fatalf("failed HasIdentity: false negative.") } - err = api.DeleteIdentity(pub) + success, err = api.DeleteKeyPair(pub) if err != nil { t.Fatalf("failed to delete second identity: %s.", err) } + if !success { + t.Fatalf("failed to delete second identity.") + } - exist, err = api.HasIdentity(pub) + exist, err = api.HasKeyPair(pub) if err != nil { t.Fatalf("failed HasIdentity(): %s.", err) } @@ -92,7 +99,7 @@ func TestBasic(t *testing.T) { id = "arbitrary text" id2 := "another arbitrary string" - exist, err = api.HasSymKey(id) + exist, err = api.HasSymmetricKey(id) if err != nil { t.Fatalf("failed HasSymKey: %s.", err) } @@ -100,12 +107,12 @@ func TestBasic(t *testing.T) { t.Fatalf("failed HasSymKey: false positive.") } - err = api.GenerateSymKey(id) + id, err = api.GenerateSymmetricKey() if err != nil { t.Fatalf("failed GenerateSymKey: %s.", err) } - exist, err = api.HasSymKey(id) + exist, err = api.HasSymmetricKey(id) if err != nil { t.Fatalf("failed HasSymKey(): %s.", err) } @@ -113,17 +120,18 @@ func TestBasic(t *testing.T) { t.Fatalf("failed HasSymKey(): false negative.") } - err = api.AddSymKey(id, []byte("some stuff here")) - if err == nil { + const password = "some stuff here" + id, err = api.AddSymmetricKeyFromPassword(password) + if err != nil { t.Fatalf("failed AddSymKey: %s.", err) } - err = api.AddSymKey(id2, []byte("some stuff here")) + id2, err = api.AddSymmetricKeyFromPassword(password) if err != nil { t.Fatalf("failed AddSymKey: %s.", err) } - exist, err = api.HasSymKey(id2) + exist, err = api.HasSymmetricKey(id2) if err != nil { t.Fatalf("failed HasSymKey(id2): %s.", err) } @@ -131,12 +139,28 @@ func TestBasic(t *testing.T) { t.Fatalf("failed HasSymKey(id2): false negative.") } - err = api.DeleteSymKey(id) + k1, err := api.GetSymmetricKey(id) + if err != nil { + t.Fatalf("failed GetSymKey(id): %s.", err) + } + k2, err := api.GetSymmetricKey(id2) + if err != nil { + t.Fatalf("failed GetSymKey(id2): %s.", err) + } + + if !bytes.Equal(k1, k2) { + t.Fatalf("installed keys are not equal") + } + + exist, err = api.DeleteSymmetricKey(id) if err != nil { t.Fatalf("failed DeleteSymKey(id): %s.", err) } + if !exist { + t.Fatalf("failed DeleteSymKey(id): false negative.") + } - exist, err = api.HasSymKey(id) + exist, err = api.HasSymmetricKey(id) if err != nil { t.Fatalf("failed HasSymKey(id): %s.", err) } @@ -147,12 +171,12 @@ func TestBasic(t *testing.T) { func TestUnmarshalFilterArgs(t *testing.T) { s := []byte(`{ - "to":"0x70c87d191324e6712a591f304b4eedef6ad9bb9d", - "from":"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", - "keyname":"testname", - "pow":2.34, + "type":"sym", + "key":"0x70c87d191324e6712a591f304b4eedef6ad9bb9d", + "signedWith":"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", + "minPoW":2.34, "topics":["0x00000000", "0x007f80ff", "0xff807f00", "0xf26e7779"], - "p2p":true + "allowP2P":true }`) var f WhisperFilterArgs @@ -161,59 +185,58 @@ func TestUnmarshalFilterArgs(t *testing.T) { t.Fatalf("failed UnmarshalJSON: %s.", err) } - if f.To != "0x70c87d191324e6712a591f304b4eedef6ad9bb9d" { - t.Fatalf("wrong To: %x.", f.To) + if !f.Symmetric { + t.Fatalf("wrong type.") } - if f.From != "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" { - t.Fatalf("wrong From: %x.", f.To) + if f.Key != "0x70c87d191324e6712a591f304b4eedef6ad9bb9d" { + t.Fatalf("wrong key: %s.", f.Key) } - if f.KeyName != "testname" { - t.Fatalf("wrong KeyName: %s.", f.KeyName) + if f.SignedWith != "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" { + t.Fatalf("wrong SignedWith: %s.", f.SignedWith) } - if f.PoW != 2.34 { - t.Fatalf("wrong pow: %f.", f.PoW) + if f.MinPoW != 2.34 { + t.Fatalf("wrong MinPoW: %f.", f.MinPoW) } - if !f.AcceptP2P { - t.Fatalf("wrong AcceptP2P: %v.", f.AcceptP2P) + if !f.AllowP2P { + t.Fatalf("wrong AllowP2P.") } if len(f.Topics) != 4 { t.Fatalf("wrong topics number: %d.", len(f.Topics)) } i := 0 - if f.Topics[i] != (TopicType{0x00, 0x00, 0x00, 0x00}) { + if !bytes.Equal(f.Topics[i], []byte{0x00, 0x00, 0x00, 0x00}) { t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i]) } i++ - if f.Topics[i] != (TopicType{0x00, 0x7f, 0x80, 0xff}) { + if !bytes.Equal(f.Topics[i], []byte{0x00, 0x7f, 0x80, 0xff}) { t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i]) } i++ - if f.Topics[i] != (TopicType{0xff, 0x80, 0x7f, 0x00}) { + if !bytes.Equal(f.Topics[i], []byte{0xff, 0x80, 0x7f, 0x00}) { t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i]) } i++ - if f.Topics[i] != (TopicType{0xf2, 0x6e, 0x77, 0x79}) { + if !bytes.Equal(f.Topics[i], []byte{0xf2, 0x6e, 0x77, 0x79}) { t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i]) } } func TestUnmarshalPostArgs(t *testing.T) { s := []byte(`{ + "type":"sym", "ttl":12345, - "from":"0x70c87d191324e6712a591f304b4eedef6ad9bb9d", - "to":"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", - "keyname":"shh_test", + "signWith":"0x70c87d191324e6712a591f304b4eedef6ad9bb9d", + "key":"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", "topic":"0xf26e7779", "padding":"0x74686973206973206D79207465737420737472696E67", "payload":"0x7061796C6F61642073686F756C642062652070736575646F72616E646F6D", - "worktime":777, - "pow":3.1416, - "filterid":"test-filter-id", - "peerid":"0xf26e7779" + "powTime":777, + "powTarget":3.1416, + "targetPeer":"enode://915533f667b1369793ebb9bda022416b1295235a1420799cd87a969467372546d808ebf59c5c9ce23f103d59b61b97df8af91f0908552485975397181b993461@127.0.0.1:12345" }`) var a PostArgs @@ -222,19 +245,20 @@ func TestUnmarshalPostArgs(t *testing.T) { t.Fatalf("failed UnmarshalJSON: %s.", err) } + if a.Type != "sym" { + t.Fatalf("wrong Type: %s.", a.Type) + } if a.TTL != 12345 { t.Fatalf("wrong ttl: %d.", a.TTL) } - if a.From != "0x70c87d191324e6712a591f304b4eedef6ad9bb9d" { - t.Fatalf("wrong From: %x.", a.To) - } - if a.To != "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" { - t.Fatalf("wrong To: %x.", a.To) + if a.SignWith != "0x70c87d191324e6712a591f304b4eedef6ad9bb9d" { + t.Fatalf("wrong From: %s.", a.SignWith) } - if a.KeyName != "shh_test" { - t.Fatalf("wrong KeyName: %s.", a.KeyName) + if a.Key != "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" { + t.Fatalf("wrong Key: %s.", a.Key) } - if a.Topic != (TopicType{0xf2, 0x6e, 0x77, 0x79}) { + + if BytesToTopic(a.Topic) != (TopicType{0xf2, 0x6e, 0x77, 0x79}) { t.Fatalf("wrong topic: %x.", a.Topic) } if string(a.Padding) != "this is my test string" { @@ -243,31 +267,34 @@ func TestUnmarshalPostArgs(t *testing.T) { if string(a.Payload) != "payload should be pseudorandom" { t.Fatalf("wrong Payload: %s.", string(a.Payload)) } - if a.WorkTime != 777 { - t.Fatalf("wrong WorkTime: %d.", a.WorkTime) - } - if a.PoW != 3.1416 { - t.Fatalf("wrong pow: %f.", a.PoW) + if a.PowTime != 777 { + t.Fatalf("wrong PowTime: %d.", a.PowTime) } - if a.FilterID != "test-filter-id" { - t.Fatalf("wrong FilterID: %s.", a.FilterID) + if a.PowTarget != 3.1416 { + t.Fatalf("wrong PowTarget: %f.", a.PowTarget) } - if !bytes.Equal(a.PeerID[:], a.Topic[:]) { - t.Fatalf("wrong PeerID: %x.", a.PeerID) + if a.TargetPeer != "enode://915533f667b1369793ebb9bda022416b1295235a1420799cd87a969467372546d808ebf59c5c9ce23f103d59b61b97df8af91f0908552485975397181b993461@127.0.0.1:12345" { + t.Fatalf("wrong PeerID: %s.", a.TargetPeer) } } -func waitForMessage(api *PublicWhisperAPI, id string, target int) bool { - for i := 0; i < 64; i++ { - all := api.GetMessages(id) - if len(all) >= target { - return true +func waitForMessages(api *PublicWhisperAPI, id string, target int) []*WhisperMessage { + // timeout: 2 seconds + result := make([]*WhisperMessage, 0, target) + for i := 0; i < 100; i++ { + mail := api.GetSubscriptionMessages(id) + if len(mail) > 0 { + for _, m := range mail { + result = append(result, m) + } + if len(result) >= target { + break + } } - time.Sleep(time.Millisecond * 16) + time.Sleep(time.Millisecond * 20) } - // timeout 1024 milliseconds - return false + return result } func TestIntegrationAsym(t *testing.T) { @@ -280,7 +307,7 @@ func TestIntegrationAsym(t *testing.T) { api.Start() defer api.Stop() - sig, err := api.NewIdentity() + sig, err := api.NewKeyPair() if err != nil { t.Fatalf("failed NewIdentity: %s.", err) } @@ -288,7 +315,7 @@ func TestIntegrationAsym(t *testing.T) { t.Fatalf("wrong signature") } - exist, err := api.HasIdentity(sig) + exist, err := api.HasKeyPair(sig) if err != nil { t.Fatalf("failed HasIdentity: %s.", err) } @@ -296,7 +323,12 @@ func TestIntegrationAsym(t *testing.T) { t.Fatalf("failed HasIdentity: false negative.") } - key, err := api.NewIdentity() + sigPubKey, err := api.GetPublicKey(sig) + if err != nil { + t.Fatalf("failed GetPublicKey: %s.", err) + } + + key, err := api.NewKeyPair() if err != nil { t.Fatalf("failed NewIdentity(): %s.", err) } @@ -304,42 +336,46 @@ func TestIntegrationAsym(t *testing.T) { t.Fatalf("wrong key") } + dstPubKey, err := api.GetPublicKey(key) + if err != nil { + t.Fatalf("failed GetPublicKey: %s.", err) + } + var topics [2]TopicType topics[0] = TopicType{0x00, 0x64, 0x00, 0xff} topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79} var f WhisperFilterArgs - f.To = key - f.From = sig - f.Topics = topics[:] - f.PoW = MinimumPoW / 2 - f.AcceptP2P = true + f.Symmetric = false + f.Key = key + f.SignedWith = sigPubKey.String() + f.Topics = make([][]byte, 2) + f.Topics[0] = topics[0][:] + f.Topics[1] = topics[1][:] + f.MinPoW = DefaultMinimumPoW / 2 + f.AllowP2P = true - id, err := api.NewFilter(f) + id, err := api.Subscribe(f) if err != nil { t.Fatalf("failed to create new filter: %s.", err) } var p PostArgs + p.Type = "asym" p.TTL = 2 - p.From = f.From - p.To = f.To + p.SignWith = sig + p.Key = dstPubKey.String() p.Padding = []byte("test string") p.Payload = []byte("extended test string") - p.PoW = MinimumPoW - p.Topic = TopicType{0xf2, 0x6e, 0x77, 0x79} - p.WorkTime = 2 + p.PowTarget = DefaultMinimumPoW + p.PowTime = 2 + p.Topic = hexutil.Bytes{0xf2, 0x6e, 0x77, 0x79} // topics[1] err = api.Post(p) if err != nil { t.Errorf("failed to post message: %s.", err) } - ok := waitForMessage(api, id, 1) - if !ok { - t.Fatalf("failed to receive first message: timeout.") - } - - mail := api.GetFilterChanges(id) + mail := waitForMessages(api, id, 1) if len(mail) != 1 { t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail)) } @@ -356,12 +392,7 @@ func TestIntegrationAsym(t *testing.T) { t.Fatalf("failed to post next message: %s.", err) } - ok = waitForMessage(api, id, 2) - if !ok { - t.Fatalf("failed to receive second message: timeout.") - } - - mail = api.GetFilterChanges(id) + mail = waitForMessages(api, id, 1) if len(mail) != 1 { t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail)) } @@ -382,21 +413,25 @@ func TestIntegrationSym(t *testing.T) { api.Start() defer api.Stop() - keyname := "schluessel" - err := api.GenerateSymKey(keyname) + symKeyID, err := api.GenerateSymmetricKey() if err != nil { t.Fatalf("failed GenerateSymKey: %s.", err) } - sig, err := api.NewIdentity() + sig, err := api.NewKeyPair() if err != nil { - t.Fatalf("failed NewIdentity: %s.", err) + t.Fatalf("failed NewKeyPair: %s.", err) } if len(sig) == 0 { t.Fatalf("wrong signature") } - exist, err := api.HasIdentity(sig) + sigPubKey, err := api.GetPublicKey(sig) + if err != nil { + t.Fatalf("failed GetPublicKey: %s.", err) + } + + exist, err := api.HasKeyPair(sig) if err != nil { t.Fatalf("failed HasIdentity: %s.", err) } @@ -408,38 +443,37 @@ func TestIntegrationSym(t *testing.T) { topics[0] = TopicType{0x00, 0x7f, 0x80, 0xff} topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79} var f WhisperFilterArgs - f.KeyName = keyname - f.Topics = topics[:] - f.PoW = 0.324 - f.From = sig - f.AcceptP2P = false + f.Symmetric = true + f.Key = symKeyID + f.Topics = make([][]byte, 2) + f.Topics[0] = topics[0][:] + f.Topics[1] = topics[1][:] + f.MinPoW = 0.324 + f.SignedWith = sigPubKey.String() + f.AllowP2P = false - id, err := api.NewFilter(f) + id, err := api.Subscribe(f) if err != nil { t.Fatalf("failed to create new filter: %s.", err) } var p PostArgs + p.Type = "sym" p.TTL = 1 - p.KeyName = keyname - p.From = f.From + p.Key = symKeyID + p.SignWith = sig p.Padding = []byte("test string") p.Payload = []byte("extended test string") - p.PoW = MinimumPoW - p.Topic = TopicType{0xf2, 0x6e, 0x77, 0x79} - p.WorkTime = 2 + p.PowTarget = DefaultMinimumPoW + p.PowTime = 2 + p.Topic = hexutil.Bytes{0xf2, 0x6e, 0x77, 0x79} err = api.Post(p) if err != nil { t.Fatalf("failed to post first message: %s.", err) } - ok := waitForMessage(api, id, 1) - if !ok { - t.Fatalf("failed to receive first message: timeout.") - } - - mail := api.GetFilterChanges(id) + mail := waitForMessages(api, id, 1) if len(mail) != 1 { t.Fatalf("failed GetFilterChanges: got %d messages.", len(mail)) } @@ -456,12 +490,7 @@ func TestIntegrationSym(t *testing.T) { t.Fatalf("failed to post second message: %s.", err) } - ok = waitForMessage(api, id, 2) - if !ok { - t.Fatalf("failed to receive second message: timeout.") - } - - mail = api.GetFilterChanges(id) + mail = waitForMessages(api, id, 1) if len(mail) != 1 { t.Fatalf("failed second GetFilterChanges: got %d messages.", len(mail)) } @@ -482,21 +511,20 @@ func TestIntegrationSymWithFilter(t *testing.T) { api.Start() defer api.Stop() - keyname := "schluessel" - err := api.GenerateSymKey(keyname) + symKeyID, err := api.GenerateSymmetricKey() if err != nil { t.Fatalf("failed to GenerateSymKey: %s.", err) } - sig, err := api.NewIdentity() + sigKeyID, err := api.NewKeyPair() if err != nil { t.Fatalf("failed NewIdentity: %s.", err) } - if len(sig) == 0 { + if len(sigKeyID) == 0 { t.Fatalf("wrong signature.") } - exist, err := api.HasIdentity(sig) + exist, err := api.HasKeyPair(sigKeyID) if err != nil { t.Fatalf("failed HasIdentity: %s.", err) } @@ -504,42 +532,46 @@ func TestIntegrationSymWithFilter(t *testing.T) { t.Fatalf("failed HasIdentity: does not exist.") } + sigPubKey, err := api.GetPublicKey(sigKeyID) + if err != nil { + t.Fatalf("failed GetPublicKey: %s.", err) + } + var topics [2]TopicType topics[0] = TopicType{0x00, 0x7f, 0x80, 0xff} topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79} var f WhisperFilterArgs - f.KeyName = keyname - f.Topics = topics[:] - f.PoW = 0.324 - f.From = sig - f.AcceptP2P = false + f.Symmetric = true + f.Key = symKeyID + f.Topics = make([][]byte, 2) + f.Topics[0] = topics[0][:] + f.Topics[1] = topics[1][:] + f.MinPoW = 0.324 + f.SignedWith = sigPubKey.String() + f.AllowP2P = false - id, err := api.NewFilter(f) + id, err := api.Subscribe(f) if err != nil { t.Fatalf("failed to create new filter: %s.", err) } var p PostArgs + p.Type = "sym" p.TTL = 1 - p.FilterID = id - p.From = sig + p.Key = symKeyID + p.SignWith = sigKeyID p.Padding = []byte("test string") p.Payload = []byte("extended test string") - p.PoW = MinimumPoW - p.Topic = TopicType{0xf2, 0x6e, 0x77, 0x79} - p.WorkTime = 2 + p.PowTarget = DefaultMinimumPoW + p.PowTime = 2 + p.Topic = hexutil.Bytes{0xf2, 0x6e, 0x77, 0x79} err = api.Post(p) if err != nil { t.Fatalf("failed to post message: %s.", err) } - ok := waitForMessage(api, id, 1) - if !ok { - t.Fatalf("failed to receive first message: timeout.") - } - - mail := api.GetFilterChanges(id) + mail := waitForMessages(api, id, 1) if len(mail) != 1 { t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail)) } @@ -556,12 +588,7 @@ func TestIntegrationSymWithFilter(t *testing.T) { t.Fatalf("failed to post next message: %s.", err) } - ok = waitForMessage(api, id, 2) - if !ok { - t.Fatalf("failed to receive second message: timeout.") - } - - mail = api.GetFilterChanges(id) + mail = waitForMessages(api, id, 1) if len(mail) != 1 { t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail)) } @@ -571,3 +598,83 @@ func TestIntegrationSymWithFilter(t *testing.T) { t.Fatalf("failed to decrypt second message: %s.", text) } } + +func TestKey(t *testing.T) { + w := New() + api := NewPublicWhisperAPI(w) + if api == nil { + t.Fatalf("failed to create API.") + } + + k, err := api.AddSymmetricKeyFromPassword("wwww") + if err != nil { + t.Fatalf("failed to create key: %s.", err) + } + + s, err := api.GetSymmetricKey(k) + if err != nil { + t.Fatalf("failed to get sym key: %s.", err) + } + + k2, err := api.AddSymmetricKeyDirect(s) + if err != nil { + t.Fatalf("failed to add sym key: %s.", err) + } + + s2, err := api.GetSymmetricKey(k2) + if err != nil { + t.Fatalf("failed to get sym key: %s.", err) + } + + if s.String() != "0x448652d595bd6ec00b2a9ea220ad6c26592d9bf4cf79023d3c1b30cb681e6e07" { + t.Fatalf("wrong key from password: %s", s.String()) + } + + if !bytes.Equal(s, s2) { + t.Fatalf("wrong key") + } +} + +func TestSubscribe(t *testing.T) { + var err error + var s string + + w := New() + api := NewPublicWhisperAPI(w) + if api == nil { + t.Fatalf("failed to create API.") + } + + symKeyID, err := api.GenerateSymmetricKey() + if err != nil { + t.Fatalf("failed to GenerateSymKey: %s.", err) + } + + var f WhisperFilterArgs + f.Symmetric = true + f.Key = symKeyID + f.Topics = make([][]byte, 5) + f.Topics[0] = []byte{0x21} + f.Topics[1] = []byte{0xd2, 0xe3} + f.Topics[2] = []byte{0x64, 0x75, 0x76} + f.Topics[3] = []byte{0xf8, 0xe9, 0xa0, 0xba} + f.Topics[4] = []byte{0xcb, 0x3c, 0xdd, 0xee, 0xff} + + s, err = api.Subscribe(f) + if err == nil { + t.Fatalf("Subscribe: false positive.") + } + + f.Topics[4] = []byte{} + if err == nil { + t.Fatalf("Subscribe: false positive again.") + } + + f.Topics[4] = []byte{0x00} + s, err = api.Subscribe(f) + if err != nil { + t.Fatalf("failed to subscribe: %s.", err) + } else { + api.Unsubscribe(s) + } +} diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go index 70c7008a7..d60868f67 100644 --- a/whisper/whisperv5/doc.go +++ b/whisper/whisperv5/doc.go @@ -54,9 +54,10 @@ const ( aesKeyLength = 32 saltLength = 12 AESNonceMaxLength = 12 + keyIdSize = 32 - MaxMessageLength = 0x0FFFFF // todo: remove this restriction after testing. this should be regulated by PoW. - MinimumPoW = 10.0 // todo: review after testing. + DefaultMaxMessageLength = 1024 * 1024 + DefaultMinimumPoW = 1.0 // todo: review after testing. padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go index 5d882d5dc..dffa7b286 100644 --- a/whisper/whisperv5/envelope.go +++ b/whisper/whisperv5/envelope.go @@ -21,7 +21,6 @@ package whisperv5 import ( "crypto/ecdsa" "encoding/binary" - "errors" "fmt" gmath "math" "math/big" @@ -83,7 +82,7 @@ func (e *Envelope) isAsymmetric() bool { } func (e *Envelope) Ver() uint64 { - return bytesToIntLittleEndian(e.Version) + return bytesToUintLittleEndian(e.Version) } // Seal closes the envelope by spending the requested amount of time as a proof @@ -95,6 +94,9 @@ func (e *Envelope) Seal(options *MessageParams) error { e.Expiry += options.WorkTime } else { target = e.powToFirstBit(options.PoW) + if target < 1 { + target = 1 + } } buf := make([]byte, 64) @@ -118,7 +120,7 @@ func (e *Envelope) Seal(options *MessageParams) error { } if target > 0 && bestBit < target { - return errors.New("Failed to reach the PoW target, insufficient work time") + return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime) } return nil diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go index ffa5ae946..03101d4a4 100644 --- a/whisper/whisperv5/filter.go +++ b/whisper/whisperv5/filter.go @@ -18,7 +18,6 @@ package whisperv5 import ( "crypto/ecdsa" - crand "crypto/rand" "fmt" "sync" @@ -30,9 +29,9 @@ type Filter struct { Src *ecdsa.PublicKey // Sender of the message KeyAsym *ecdsa.PrivateKey // Private Key of recipient KeySym []byte // Key associated with the Topic - Topics []TopicType // Topics to filter messages with + Topics [][]byte // Topics to filter messages with PoW float64 // Proof of work as described in the Whisper spec - AcceptP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages + AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization Messages map[common.Hash]*ReceivedMessage @@ -52,47 +51,35 @@ func NewFilters(w *Whisper) *Filters { } } -func (fs *Filters) generateRandomID() (id string, err error) { - buf := make([]byte, 20) - for i := 0; i < 3; i++ { - _, err = crand.Read(buf) - if err != nil { - continue - } - if !validateSymmetricKey(buf) { - err = fmt.Errorf("error in generateRandomID: crypto/rand failed to generate random data") - continue - } - id = common.Bytes2Hex(buf) - if fs.watchers[id] != nil { - err = fmt.Errorf("error in generateRandomID: generated same ID twice") - continue - } - return id, err - } - - return "", err -} - func (fs *Filters) Install(watcher *Filter) (string, error) { if watcher.Messages == nil { watcher.Messages = make(map[common.Hash]*ReceivedMessage) } + id, err := GenerateRandomID() + if err != nil { + return "", err + } + fs.mutex.Lock() defer fs.mutex.Unlock() - id, err := fs.generateRandomID() - if err == nil { - fs.watchers[id] = watcher + if fs.watchers[id] != nil { + return "", fmt.Errorf("failed to generate unique ID") } + + fs.watchers[id] = watcher return id, err } -func (fs *Filters) Uninstall(id string) { +func (fs *Filters) Uninstall(id string) bool { fs.mutex.Lock() defer fs.mutex.Unlock() - delete(fs.watchers, id) + if fs.watchers[id] != nil { + delete(fs.watchers, id) + return true + } + return false } func (fs *Filters) Get(id string) *Filter { @@ -102,11 +89,16 @@ func (fs *Filters) Get(id string) *Filter { } func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { - fs.mutex.RLock() var msg *ReceivedMessage - for j, watcher := range fs.watchers { - if p2pMessage && !watcher.AcceptP2P { - log.Trace(fmt.Sprintf("msg [%x], filter [%s]: p2p messages are not allowed", env.Hash(), j)) + + fs.mutex.RLock() + defer fs.mutex.RUnlock() + + i := -1 // only used for logging info + for _, watcher := range fs.watchers { + i++ + if p2pMessage && !watcher.AllowP2P { + log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i)) continue } @@ -118,22 +110,32 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { if match { msg = env.Open(watcher) if msg == nil { - log.Trace(fmt.Sprintf("msg [%x], filter [%s]: failed to open", env.Hash(), j)) + log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i) } } else { - log.Trace(fmt.Sprintf("msg [%x], filter [%s]: does not match", env.Hash(), j)) + log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i) } } if match && msg != nil { + log.Trace("processing message: decrypted", "hash", env.Hash().Hex()) watcher.Trigger(msg) } } - fs.mutex.RUnlock() // we need to unlock before calling addDecryptedMessage +} - if msg != nil { - fs.whisper.addDecryptedMessage(msg) +func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage { + if f.MatchEnvelope(env) { + msg := env.Open(f) + if msg != nil { + return msg + } else { + log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex()) + } + } else { + log.Trace("processing envelope: does not match", "hash", env.Hash().Hex()) } + return nil } func (f *Filter) expectsAsymmetricEncryption() bool { @@ -200,20 +202,33 @@ func (f *Filter) MatchTopic(topic TopicType) bool { return true } - for _, t := range f.Topics { - if t == topic { + for _, bt := range f.Topics { + if matchSingleTopic(topic, bt) { return true } } return false } +func matchSingleTopic(topic TopicType, bt []byte) bool { + if len(bt) > 4 { + bt = bt[:4] + } + + for j, b := range bt { + if topic[j] != b { + return false + } + } + return true +} + func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { if !ValidatePublicKey(a) { return false } else if !ValidatePublicKey(b) { return false } - // the Curve is always the same, just compare the points + // the curve is always the same, just compare the points return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 } diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go index d69fb40db..ae21d1739 100644 --- a/whisper/whisperv5/filter_test.go +++ b/whisper/whisperv5/filter_test.go @@ -18,7 +18,7 @@ package whisperv5 import ( "math/big" - "math/rand" + mrand "math/rand" "testing" "time" @@ -33,12 +33,12 @@ var seed int64 // reproduciblity independent of their sequence. func InitSingleTest() { seed = time.Now().Unix() - rand.Seed(seed) + mrand.Seed(seed) } func InitDebugTest(i int64) { seed = i - rand.Seed(seed) + mrand.Seed(seed) } type FilterTestCase struct { @@ -53,9 +53,10 @@ func generateFilter(t *testing.T, symmetric bool) (*Filter, error) { f.Messages = make(map[common.Hash]*ReceivedMessage) const topicNum = 8 - f.Topics = make([]TopicType, topicNum) + f.Topics = make([][]byte, topicNum) for i := 0; i < topicNum; i++ { - randomize(f.Topics[i][:]) + f.Topics[i] = make([]byte, 4) + mrand.Read(f.Topics[i][:]) f.Topics[i][0] = 0x01 } @@ -68,7 +69,7 @@ func generateFilter(t *testing.T, symmetric bool) (*Filter, error) { if symmetric { f.KeySym = make([]byte, 12) - randomize(f.KeySym) + mrand.Read(f.KeySym) f.SymKeyHash = crypto.Keccak256Hash(f.KeySym) } else { f.KeyAsym, err = crypto.GenerateKey() @@ -87,7 +88,7 @@ func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase { for i := 0; i < SizeTestFilters; i++ { f, _ := generateFilter(t, true) cases[i].f = f - cases[i].alive = (rand.Int()&int(1) == 0) + cases[i].alive = (mrand.Int()&int(1) == 0) } return cases } @@ -108,7 +109,7 @@ func TestInstallFilters(t *testing.T) { t.Fatalf("seed %d: failed to install filter: %s", seed, err) } tst[i].id = j - if len(j) != 40 { + if len(j) != keyIdSize*2 { t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j)) } } @@ -147,7 +148,7 @@ func TestComparePubKey(t *testing.T) { } // generate key3 == key1 - rand.Seed(seed) + mrand.Seed(seed) key3, err := crypto.GenerateKey() if err != nil { t.Fatalf("failed to generate third key with seed %d: %s.", seed, err) @@ -193,9 +194,9 @@ func TestMatchEnvelope(t *testing.T) { } // encrypt symmetrically - i := rand.Int() % 4 - fsym.Topics[i] = params.Topic - fasym.Topics[i] = params.Topic + i := mrand.Int() % 4 + fsym.Topics[i] = params.Topic[:] + fasym.Topics[i] = params.Topic[:] msg = NewSentMessage(params) env, err = msg.Wrap(params) if err != nil { @@ -320,7 +321,7 @@ func TestMatchMessageSym(t *testing.T) { const index = 1 params.KeySym = f.KeySym - params.Topic = f.Topics[index] + params.Topic = BytesToTopic(f.Topics[index]) sentMessage := NewSentMessage(params) env, err := sentMessage.Wrap(params) @@ -413,7 +414,7 @@ func TestMatchMessageAsym(t *testing.T) { } const index = 1 - params.Topic = f.Topics[index] + params.Topic = BytesToTopic(f.Topics[index]) params.Dst = &f.KeyAsym.PublicKey keySymOrig := params.KeySym params.KeySym = nil @@ -491,7 +492,7 @@ func cloneFilter(orig *Filter) *Filter { clone.KeySym = orig.KeySym clone.Topics = orig.Topics clone.PoW = orig.PoW - clone.AcceptP2P = orig.AcceptP2P + clone.AllowP2P = orig.AllowP2P clone.SymKeyHash = orig.SymKeyHash return &clone } @@ -504,7 +505,7 @@ func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope { } params.KeySym = f.KeySym - params.Topic = f.Topics[2] + params.Topic = BytesToTopic(f.Topics[2]) sentMessage := NewSentMessage(params) env, err := sentMessage.Wrap(params) if err != nil { @@ -544,7 +545,7 @@ func TestWatchers(t *testing.T) { var envelopes [NumMessages]*Envelope for i = 0; i < NumMessages; i++ { - j = rand.Uint32() % NumFilters + j = mrand.Uint32() % NumFilters e = generateCompatibeEnvelope(t, tst[j].f) envelopes[i] = e tst[j].msgCnt++ @@ -597,7 +598,7 @@ func TestWatchers(t *testing.T) { envelopes[0] = e tst[0].msgCnt++ for i = 1; i < NumMessages; i++ { - j = rand.Uint32() % NumFilters + j = mrand.Uint32() % NumFilters e = generateCompatibeEnvelope(t, tst[j].f) envelopes[i] = e tst[j].msgCnt++ @@ -655,7 +656,7 @@ func TestWatchers(t *testing.T) { if f == nil { t.Fatalf("failed to get the filter with seed %d.", seed) } - f.AcceptP2P = true + f.AllowP2P = true total = 0 filters.NotifyWatchers(envelopes[0], true) @@ -668,3 +669,40 @@ func TestWatchers(t *testing.T) { t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total) } } + +func TestVariableTopics(t *testing.T) { + InitSingleTest() + + var match bool + params, err := generateMessageParams() + if err != nil { + t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) + } + msg := NewSentMessage(params) + env, err := msg.Wrap(params) + if err != nil { + t.Fatalf("failed Wrap with seed %d: %s.", seed, err) + } + + f, err := generateFilter(t, true) + if err != nil { + t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) + } + + for i := 0; i < 4; i++ { + arr := make([]byte, i+1, 4) + copy(arr, env.Topic[:i+1]) + + f.Topics[4] = arr + match = f.MatchEnvelope(env) + if !match { + t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) + } + + f.Topics[4][i]++ + match = f.MatchEnvelope(env) + if match { + t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i) + } + } +} diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go index 9677f278e..9b9c389a6 100644 --- a/whisper/whisperv5/message.go +++ b/whisper/whisperv5/message.go @@ -25,8 +25,6 @@ import ( crand "crypto/rand" "crypto/sha256" "errors" - "fmt" - mrand "math/rand" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -102,14 +100,18 @@ func NewSentMessage(params *MessageParams) *SentMessage { msg := SentMessage{} msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimitUpper) msg.Raw[0] = 0 // set all the flags to zero - msg.appendPadding(params) + err := msg.appendPadding(params) + if err != nil { + log.Error("failed to create NewSentMessage", "err", err) + return nil + } msg.Raw = append(msg.Raw, params.Payload...) return &msg } // appendPadding appends the pseudorandom padding bytes and sets the padding flag. // The last byte contains the size of padding (thus, its size must not exceed 256). -func (msg *SentMessage) appendPadding(params *MessageParams) { +func (msg *SentMessage) appendPadding(params *MessageParams) error { total := len(params.Payload) + 1 if params.Src != nil { total += signatureLength @@ -128,7 +130,10 @@ func (msg *SentMessage) appendPadding(params *MessageParams) { panic("please fix the padding algorithm before releasing new version") } buf := make([]byte, padSize) - randomize(buf[1:]) + _, err := crand.Read(buf[1:]) + if err != nil { + return err + } buf[0] = byte(padSize) if params.Padding != nil { copy(buf[1:], params.Padding) @@ -136,6 +141,7 @@ func (msg *SentMessage) appendPadding(params *MessageParams) { msg.Raw = append(msg.Raw, buf...) msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size } + return nil } // sign calculates and sets the cryptographic signature for the message, @@ -143,7 +149,7 @@ func (msg *SentMessage) appendPadding(params *MessageParams) { func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error { if isMessageSigned(msg.Raw[0]) { // this should not happen, but no reason to panic - log.Error(fmt.Sprintf("Trying to sign a message which was already signed")) + log.Error("failed to sign the message: already signed") return nil } @@ -161,7 +167,7 @@ func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error { // encryptAsymmetric encrypts a message with a public key. func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { if !ValidatePublicKey(key) { - return fmt.Errorf("Invalid public key provided for asymmetric encryption") + return errors.New("invalid public key provided for asymmetric encryption") } encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil) if err == nil { @@ -215,17 +221,6 @@ func (msg *SentMessage) encryptSymmetric(key []byte) (salt []byte, nonce []byte, } // Wrap bundles the message into an Envelope to transmit over the network. -// -// pow (Proof Of Work) controls how much time to spend on hashing the message, -// inherently controlling its priority through the network (smaller hash, bigger -// priority). -// -// The user can control the amount of identity, privacy and encryption through -// the options parameter as follows: -// - options.From == nil && options.To == nil: anonymous broadcast -// - options.From != nil && options.To == nil: signed broadcast (known sender) -// - options.From == nil && options.To != nil: encrypted anonymous message -// - options.From != nil && options.To != nil: encrypted signed message func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { if options.TTL == 0 { options.TTL = DefaultTTL @@ -236,17 +231,13 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er return nil, err } } - if len(msg.Raw) > MaxMessageLength { - log.Error(fmt.Sprintf("Message size must not exceed %d bytes", MaxMessageLength)) - return nil, errors.New("Oversized message") - } var salt, nonce []byte if options.Dst != nil { err = msg.encryptAsymmetric(options.Dst) } else if options.KeySym != nil { salt, nonce, err = msg.encryptSymmetric(options.KeySym) } else { - err = errors.New("Unable to encrypt the message: neither Dst nor Key") + err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") } if err != nil { @@ -258,7 +249,6 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er if err != nil { return nil, err } - return envelope, nil } @@ -279,9 +269,8 @@ func (msg *ReceivedMessage) decryptSymmetric(key []byte, salt []byte, nonce []by return err } if len(nonce) != aesgcm.NonceSize() { - info := fmt.Sprintf("Wrong AES nonce size - want: %d, got: %d", len(nonce), aesgcm.NonceSize()) - log.Error(fmt.Sprintf(info)) - return errors.New(info) + log.Error("decrypting the message", "AES nonce size", len(nonce)) + return errors.New("wrong AES nonce size") } decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil) if err != nil { @@ -336,7 +325,7 @@ func (msg *ReceivedMessage) extractPadding(end int) (int, bool) { paddingSize := 0 sz := int(msg.Raw[0] & paddingMask) // number of bytes containing the entire size of padding, could be zero if sz != 0 { - paddingSize = int(bytesToIntLittleEndian(msg.Raw[1 : 1+sz])) + paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz])) if paddingSize < sz || paddingSize+1 > end { return 0, false } @@ -351,7 +340,7 @@ func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { pub, err := crypto.SigToPub(msg.hash(), msg.Signature) if err != nil { - log.Error(fmt.Sprintf("Could not get public key from signature: %v", err)) + log.Error("failed to recover public key from signature", "err", err) return nil } return pub @@ -365,19 +354,3 @@ func (msg *ReceivedMessage) hash() []byte { } return crypto.Keccak256(msg.Raw) } - -// rand.Rand provides a Read method in Go 1.7 and later, -// but we can't use it yet. -func randomize(b []byte) { - cnt := 0 - val := mrand.Int63() - for n := 0; n < len(b); n++ { - b[n] = byte(val) - val >>= 8 - cnt++ - if cnt >= 7 { - cnt = 0 - val = mrand.Int63() - } - } -} diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go index c6f1ca2ca..1ed7250d3 100644 --- a/whisper/whisperv5/message_test.go +++ b/whisper/whisperv5/message_test.go @@ -18,7 +18,7 @@ package whisperv5 import ( "bytes" - "math/rand" + mrand "math/rand" "testing" "github.com/ethereum/go-ethereum/crypto" @@ -34,13 +34,13 @@ func generateMessageParams() (*MessageParams, error) { // set all the parameters except p.Dst buf := make([]byte, 1024) - randomize(buf) - sz := rand.Intn(400) + mrand.Read(buf) + sz := mrand.Intn(400) var p MessageParams p.PoW = 0.01 p.WorkTime = 1 - p.TTL = uint32(rand.Intn(1024)) + p.TTL = uint32(mrand.Intn(1024)) p.Payload = make([]byte, sz) p.Padding = make([]byte, padSizeLimitUpper) p.KeySym = make([]byte, aesKeyLength) @@ -132,7 +132,7 @@ func TestMessageEncryption(t *testing.T) { func TestMessageWrap(t *testing.T) { seed = int64(1777444222) - rand.Seed(seed) + mrand.Seed(seed) target := 128.0 params, err := generateMessageParams() @@ -168,7 +168,7 @@ func TestMessageWrap(t *testing.T) { func TestMessageSeal(t *testing.T) { // this test depends on deterministic choice of seed (1976726903) seed = int64(1976726903) - rand.Seed(seed) + mrand.Seed(seed) params, err := generateMessageParams() if err != nil { @@ -179,8 +179,8 @@ func TestMessageSeal(t *testing.T) { params.TTL = 1 aesnonce := make([]byte, 12) salt := make([]byte, 12) - randomize(aesnonce) - randomize(salt) + mrand.Read(aesnonce) + mrand.Read(salt) env := NewEnvelope(params.TTL, params.Topic, salt, aesnonce, msg) if err != nil { diff --git a/whisper/whisperv5/peer.go b/whisper/whisperv5/peer.go index 315401aea..184c4ebf8 100644 --- a/whisper/whisperv5/peer.go +++ b/whisper/whisperv5/peer.go @@ -55,13 +55,13 @@ func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer { // into the network. func (p *Peer) start() { go p.update() - log.Debug(fmt.Sprintf("%v: whisper started", p.peer)) + log.Trace("start", "peer", p.ID()) } // stop terminates the peer updater, stopping message forwarding to it. func (p *Peer) stop() { close(p.quit) - log.Debug(fmt.Sprintf("%v: whisper stopped", p.peer)) + log.Trace("stop", "peer", p.ID()) } // handshake sends the protocol initiation status message to the remote peer and @@ -78,19 +78,19 @@ func (p *Peer) handshake() error { return err } if packet.Code != statusCode { - return fmt.Errorf("peer sent %x before status packet", packet.Code) + return fmt.Errorf("peer [%x] sent packet %x before status packet", p.ID(), packet.Code) } s := rlp.NewStream(packet.Payload, uint64(packet.Size)) peerVersion, err := s.Uint() if err != nil { - return fmt.Errorf("bad status message: %v", err) + return fmt.Errorf("peer [%x] sent bad status message: %v", p.ID(), err) } if peerVersion != ProtocolVersion { - return fmt.Errorf("protocol version mismatch %d != %d", peerVersion, ProtocolVersion) + return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", p.ID(), peerVersion, ProtocolVersion) } // Wait until out own status is consumed too if err := <-errc; err != nil { - return fmt.Errorf("failed to send status packet: %v", err) + return fmt.Errorf("peer [%x] failed to send status packet: %v", p.ID(), err) } return nil } @@ -110,7 +110,7 @@ func (p *Peer) update() { case <-transmit.C: if err := p.broadcast(); err != nil { - log.Info(fmt.Sprintf("%v: broadcast failed: %v", p.peer, err)) + log.Trace("broadcast failed", "reason", err, "peer", p.ID()) return } @@ -165,7 +165,7 @@ func (p *Peer) broadcast() error { if err := p2p.Send(p.ws, messagesCode, transmit); err != nil { return err } - log.Trace(fmt.Sprint(p.peer, "broadcasted", len(transmit), "message(s)")) + log.Trace("broadcast", "num. messages", len(transmit)) return nil } diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go index e3073bc6c..a79b6ad14 100644 --- a/whisper/whisperv5/peer_test.go +++ b/whisper/whisperv5/peer_test.go @@ -114,12 +114,13 @@ func initialize(t *testing.T) { for i := 0; i < NumNodes; i++ { var node TestNode node.shh = New() - node.shh.test = true + node.shh.SetMinimumPoW(0.00000001) node.shh.Start(nil) topics := make([]TopicType, 0) topics = append(topics, sharedTopic) - f := Filter{KeySym: sharedKey, Topics: topics} - node.filerId, err = node.shh.Watch(&f) + f := Filter{KeySym: sharedKey} + f.Topics = [][]byte{topics[0][:]} + node.filerId, err = node.shh.Subscribe(&f) if err != nil { t.Fatalf("failed to install the filter: %s.", err) } @@ -166,7 +167,7 @@ func stopServers() { for i := 0; i < NumNodes; i++ { n := nodes[i] if n != nil { - n.shh.Unwatch(n.filerId) + n.shh.Unsubscribe(n.filerId) n.shh.Stop() n.server.Stop() } @@ -257,7 +258,7 @@ func sendMsg(t *testing.T, expected bool, id int) { return } - opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001} + opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001, WorkTime: 1} if !expected { opt.KeySym[0]++ opt.Topic[0]++ @@ -267,12 +268,12 @@ func sendMsg(t *testing.T, expected bool, id int) { msg := NewSentMessage(&opt) envelope, err := msg.Wrap(&opt) if err != nil { - t.Fatalf("failed to seal message.") + t.Fatalf("failed to seal message: %s", err) } err = nodes[id].shh.Send(envelope) if err != nil { - t.Fatalf("failed to send message.") + t.Fatalf("failed to send message: %s", err) } } diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index 5062f7b6b..c4d5d04a7 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -31,59 +31,62 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" + "github.com/syndtr/goleveldb/leveldb/errors" "golang.org/x/crypto/pbkdf2" set "gopkg.in/fatih/set.v0" ) type Statistics struct { - messagesCleared int - memoryCleared int - totalMemoryUsed int + messagesCleared int + memoryCleared int + memoryUsed int + cycles int + totalMessagesCleared int } // Whisper represents a dark communication interface through the Ethereum // network, using its very own P2P communication layer. type Whisper struct { - protocol p2p.Protocol - filters *Filters + protocol p2p.Protocol // Protocol description and parameters + filters *Filters // Message filters installed with Subscribe function - privateKeys map[string]*ecdsa.PrivateKey - symKeys map[string][]byte - keyMu sync.RWMutex + privateKeys map[string]*ecdsa.PrivateKey // Private key storage + symKeys map[string][]byte // Symmetric key storage + keyMu sync.RWMutex // Mutex associated with key storages - envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node - messages map[common.Hash]*ReceivedMessage // Pool of successfully decrypted messages, which are not expired yet - expirations map[uint32]*set.SetNonTS // Message expiration pool - poolMu sync.RWMutex // Mutex to sync the message and expiration pools + envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node + expirations map[uint32]*set.SetNonTS // Message expiration pool + poolMu sync.RWMutex // Mutex to sync the message and expiration pools peers map[*Peer]struct{} // Set of currently active peers peerMu sync.RWMutex // Mutex to sync the active peer set - mailServer MailServer + messageQueue chan *Envelope // Message queue for normal whisper messages + p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further) + quit chan struct{} // Channel used for graceful exit - messageQueue chan *Envelope - p2pMsgQueue chan *Envelope - quit chan struct{} + minPoW float64 // Minimal PoW required by the whisper node + maxMsgLength int // Maximal message length allowed by the whisper node + overflow bool // Indicator of message queue overflow - stats Statistics + stats Statistics // Statistics of whisper node - overflow bool - test bool + mailServer MailServer // MailServer interface } // New creates a Whisper client ready to communicate through the Ethereum P2P network. -// Param s should be passed if you want to implement mail server, otherwise nil. func New() *Whisper { whisper := &Whisper{ privateKeys: make(map[string]*ecdsa.PrivateKey), symKeys: make(map[string][]byte), envelopes: make(map[common.Hash]*Envelope), - messages: make(map[common.Hash]*ReceivedMessage), expirations: make(map[uint32]*set.SetNonTS), peers: make(map[*Peer]struct{}), messageQueue: make(chan *Envelope, messageQueueLimit), p2pMsgQueue: make(chan *Envelope, messageQueueLimit), quit: make(chan struct{}), + minPoW: DefaultMinimumPoW, + maxMsgLength: DefaultMaxMessageLength, } whisper.filters = NewFilters(whisper) @@ -110,6 +113,8 @@ func (w *Whisper) APIs() []rpc.API { } } +// RegisterServer registers MailServer interface. +// MailServer will process all the incoming messages with p2pRequestCode. func (w *Whisper) RegisterServer(server MailServer) { w.mailServer = server } @@ -124,6 +129,25 @@ func (w *Whisper) Version() uint { return w.protocol.Version } +// SetMaxMessageLength sets the maximal message length allowed by this node +func (w *Whisper) SetMaxMessageLength(val int) error { + if val <= 0 { + return fmt.Errorf("invalid message length: %d", val) + } + w.maxMsgLength = val + return nil +} + +// SetMinimumPoW sets the minimal PoW required by this node +func (w *Whisper) SetMinimumPoW(val float64) error { + if val <= 0.0 { + return fmt.Errorf("invalid PoW: %f", val) + } + w.minPoW = val + return nil +} + +// getPeer retrieves peer by ID func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { w.peerMu.Lock() defer w.peerMu.Unlock() @@ -136,9 +160,9 @@ func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { return nil, fmt.Errorf("Could not find peer with ID: %x", peerID) } -// MarkPeerTrusted marks specific peer trusted, which will allow it -// to send historic (expired) messages. -func (w *Whisper) MarkPeerTrusted(peerID []byte) error { +// AllowP2PMessagesFromPeer marks specific peer trusted, +// which will allow it to send historic (expired) messages. +func (w *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error { p, err := w.getPeer(peerID) if err != nil { return err @@ -147,6 +171,11 @@ func (w *Whisper) MarkPeerTrusted(peerID []byte) error { return nil } +// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer, +// which is known to implement MailServer interface, and is supposed to process this +// request and respond with a number of peer-to-peer messages (possibly expired), +// which are not supposed to be forwarded any further. +// The whisper protocol is agnostic of the format and contents of envelope. func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error { p, err := w.getPeer(peerID) if err != nil { @@ -156,153 +185,226 @@ func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) err return p2p.Send(p.ws, p2pRequestCode, envelope) } +// SendP2PMessage sends a peer-to-peer message to a specific peer. func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { p, err := w.getPeer(peerID) if err != nil { return err } - return p2p.Send(p.ws, p2pCode, envelope) + return w.SendP2PDirect(p, envelope) } +// SendP2PDirect sends a peer-to-peer message to a specific peer. func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error { return p2p.Send(peer.ws, p2pCode, envelope) } -// NewIdentity generates a new cryptographic identity for the client, and injects -// it into the known identities for message decryption. -func (w *Whisper) NewIdentity() *ecdsa.PrivateKey { +// NewKeyPair generates a new cryptographic identity for the client, and injects +// it into the known identities for message decryption. Returns ID of the new key pair. +func (w *Whisper) NewKeyPair() (string, error) { key, err := crypto.GenerateKey() if err != nil || !validatePrivateKey(key) { key, err = crypto.GenerateKey() // retry once } if err != nil { - panic(err) + return "", err } if !validatePrivateKey(key) { - panic("Failed to generate valid key") + return "", fmt.Errorf("failed to generate valid key") } + + id, err := GenerateRandomID() + if err != nil { + return "", fmt.Errorf("failed to generate ID: %s", err) + } + w.keyMu.Lock() defer w.keyMu.Unlock() - w.privateKeys[common.ToHex(crypto.FromECDSAPub(&key.PublicKey))] = key - return key + + if w.privateKeys[id] != nil { + return "", fmt.Errorf("failed to generate unique ID") + } + w.privateKeys[id] = key + return id, nil } -// DeleteIdentity deletes the specified key if it exists. -func (w *Whisper) DeleteIdentity(key string) { +// DeleteKeyPair deletes the specified key if it exists. +func (w *Whisper) DeleteKeyPair(key string) bool { w.keyMu.Lock() defer w.keyMu.Unlock() - delete(w.privateKeys, key) + + if w.privateKeys[key] != nil { + delete(w.privateKeys, key) + return true + } + return false } -// HasIdentity checks if the the whisper node is configured with the private key +// HasKeyPair checks if the the whisper node is configured with the private key // of the specified public pair. -func (w *Whisper) HasIdentity(pubKey string) bool { +func (w *Whisper) HasKeyPair(id string) bool { w.keyMu.RLock() defer w.keyMu.RUnlock() - return w.privateKeys[pubKey] != nil + return w.privateKeys[id] != nil } -// GetIdentity retrieves the private key of the specified public identity. -func (w *Whisper) GetIdentity(pubKey string) *ecdsa.PrivateKey { +// GetPrivateKey retrieves the private key of the specified identity. +func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { w.keyMu.RLock() defer w.keyMu.RUnlock() - return w.privateKeys[pubKey] + key := w.privateKeys[id] + if key == nil { + return nil, fmt.Errorf("invalid id") + } + return key, nil } -func (w *Whisper) GenerateSymKey(name string) error { +// GenerateSymKey generates a random symmetric key and stores it under id, +// which is then returned. Will be used in the future for session key exchange. +func (w *Whisper) GenerateSymKey() (string, error) { const size = aesKeyLength * 2 buf := make([]byte, size) _, err := crand.Read(buf) if err != nil { - return err + return "", err } else if !validateSymmetricKey(buf) { - return fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data") + return "", fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data") } key := buf[:aesKeyLength] salt := buf[aesKeyLength:] derived, err := DeriveOneTimeKey(key, salt, EnvelopeVersion) if err != nil { - return err + return "", err } else if !validateSymmetricKey(derived) { - return fmt.Errorf("failed to derive valid key") + return "", fmt.Errorf("failed to derive valid key") + } + + id, err := GenerateRandomID() + if err != nil { + return "", fmt.Errorf("failed to generate ID: %s", err) } w.keyMu.Lock() defer w.keyMu.Unlock() - if w.symKeys[name] != nil { - return fmt.Errorf("Key with name [%s] already exists", name) + if w.symKeys[id] != nil { + return "", fmt.Errorf("failed to generate unique ID") } - w.symKeys[name] = derived - return nil + w.symKeys[id] = derived + return id, nil } -func (w *Whisper) AddSymKey(name string, key []byte) error { - if w.HasSymKey(name) { - return fmt.Errorf("Key with name [%s] already exists", name) +// AddSymKeyDirect stores the key, and returns its id. +func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) { + if len(key) != aesKeyLength { + return "", fmt.Errorf("wrong key size: %d", len(key)) } - derived, err := deriveKeyMaterial(key, EnvelopeVersion) + id, err := GenerateRandomID() if err != nil { - return err + return "", fmt.Errorf("failed to generate ID: %s", err) } w.keyMu.Lock() defer w.keyMu.Unlock() - // double check is necessary, because deriveKeyMaterial() is slow - if w.symKeys[name] != nil { - return fmt.Errorf("Key with name [%s] already exists", name) + if w.symKeys[id] != nil { + return "", fmt.Errorf("failed to generate unique ID") } - w.symKeys[name] = derived - return nil + w.symKeys[id] = key + return id, nil } -func (w *Whisper) HasSymKey(name string) bool { +// AddSymKeyFromPassword generates the key from password, stores it, and returns its id. +func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) { + id, err := GenerateRandomID() + if err != nil { + return "", fmt.Errorf("failed to generate ID: %s", err) + } + if w.HasSymKey(id) { + return "", fmt.Errorf("failed to generate unique ID") + } + + derived, err := deriveKeyMaterial([]byte(password), EnvelopeVersion) + if err != nil { + return "", err + } + + w.keyMu.Lock() + defer w.keyMu.Unlock() + + // double check is necessary, because deriveKeyMaterial() is very slow + if w.symKeys[id] != nil { + return "", fmt.Errorf("critical error: failed to generate unique ID") + } + w.symKeys[id] = derived + return id, nil +} + +// HasSymKey returns true if there is a key associated with the given id. +// Otherwise returns false. +func (w *Whisper) HasSymKey(id string) bool { w.keyMu.RLock() defer w.keyMu.RUnlock() - return w.symKeys[name] != nil + return w.symKeys[id] != nil } -func (w *Whisper) DeleteSymKey(name string) { +// DeleteSymKey deletes the key associated with the name string if it exists. +func (w *Whisper) DeleteSymKey(id string) bool { w.keyMu.Lock() defer w.keyMu.Unlock() - delete(w.symKeys, name) + if w.symKeys[id] != nil { + delete(w.symKeys, id) + return true + } + return false } -func (w *Whisper) GetSymKey(name string) []byte { +// GetSymKey returns the symmetric key associated with the given id. +func (w *Whisper) GetSymKey(id string) ([]byte, error) { w.keyMu.RLock() defer w.keyMu.RUnlock() - return w.symKeys[name] + if w.symKeys[id] != nil { + return w.symKeys[id], nil + } + return nil, fmt.Errorf("non-existent key ID") } -// Watch installs a new message handler to run in case a matching packet arrives -// from the whisper network. -func (w *Whisper) Watch(f *Filter) (string, error) { +// Subscribe installs a new message handler used for filtering, decrypting +// and subsequent storing of incoming messages. +func (w *Whisper) Subscribe(f *Filter) (string, error) { return w.filters.Install(f) } +// GetFilter returns the filter by id. func (w *Whisper) GetFilter(id string) *Filter { return w.filters.Get(id) } -// Unwatch removes an installed message handler. -func (w *Whisper) Unwatch(id string) { - w.filters.Uninstall(id) +// Unsubscribe removes an installed message handler. +func (w *Whisper) Unsubscribe(id string) error { + ok := w.filters.Uninstall(id) + if !ok { + return fmt.Errorf("Unsubscribe: Invalid ID") + } + return nil } // Send injects a message into the whisper send queue, to be distributed in the // network in the coming cycles. func (w *Whisper) Send(envelope *Envelope) error { - _, err := w.add(envelope) + ok, err := w.add(envelope) + if !ok { + return fmt.Errorf("failed to add envelope") + } return err } // Start implements node.Service, starting the background data propagation thread // of the Whisper protocol. func (w *Whisper) Start(*p2p.Server) error { - log.Info(fmt.Sprint("Whisper started")) + log.Info("started whisper v." + ProtocolVersionStr) go w.update() numCPU := runtime.NumCPU() @@ -317,11 +419,11 @@ func (w *Whisper) Start(*p2p.Server) error { // of the Whisper protocol. func (w *Whisper) Stop() error { close(w.quit) - log.Info(fmt.Sprint("Whisper stopped")) + log.Info("whisper stopped") return nil } -// handlePeer is called by the underlying P2P layer when the whisper sub-protocol +// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol // connection is negotiated. func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { // Create the new peer and start tracking it @@ -353,26 +455,31 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { // fetch the next packet packet, err := rw.ReadMsg() if err != nil { + log.Warn("message loop", "peer", p.peer.ID(), "err", err) return err } + if packet.Size > uint32(wh.maxMsgLength) { + log.Warn("oversized message received", "peer", p.peer.ID()) + return errors.New("oversized message received") + } switch packet.Code { case statusCode: // this should not happen, but no need to panic; just ignore this message. - log.Warn(fmt.Sprintf("%v: unxepected status message received", p.peer)) + log.Warn("unxepected status message received", "peer", p.peer.ID()) case messagesCode: // decode the contained envelopes var envelopes []*Envelope if err := packet.Decode(&envelopes); err != nil { - log.Warn(fmt.Sprintf("%v: failed to decode envelope: [%v], peer will be disconnected", p.peer, err)) - return fmt.Errorf("garbage received") + log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err) + return errors.New("invalid envelope") } // inject all envelopes into the internal pool for _, envelope := range envelopes { cached, err := wh.add(envelope) if err != nil { - log.Warn(fmt.Sprintf("%v: bad envelope received: [%v], peer will be disconnected", p.peer, err)) - return fmt.Errorf("invalid envelope") + log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err) + return errors.New("invalid envelope") } if cached { p.mark(envelope) @@ -386,8 +493,8 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { if p.trusted { var envelope Envelope if err := packet.Decode(&envelope); err != nil { - log.Warn(fmt.Sprintf("%v: failed to decode direct message: [%v], peer will be disconnected", p.peer, err)) - return fmt.Errorf("garbage received (directMessage)") + log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err) + return errors.New("invalid direct message") } wh.postEvent(&envelope, true) } @@ -396,8 +503,8 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { if wh.mailServer != nil { var request Envelope if err := packet.Decode(&request); err != nil { - log.Warn(fmt.Sprintf("%v: failed to decode p2p request message: [%v], peer will be disconnected", p.peer, err)) - return fmt.Errorf("garbage received (p2p request)") + log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err) + return errors.New("invalid p2p request") } wh.mailServer.DeliverMail(p, &request) } @@ -430,12 +537,12 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) { if envelope.Expiry+SynchAllowance*2 < now { return false, fmt.Errorf("very old message") } else { - log.Debug(fmt.Sprintf("expired envelope dropped [%x]", envelope.Hash())) + log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex()) return false, nil // drop envelope without error } } - if len(envelope.Data) > MaxMessageLength { + if envelope.size() > wh.maxMsgLength { return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) } @@ -453,8 +560,8 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) { return false, fmt.Errorf("oversized salt [%x]", envelope.Hash()) } - if envelope.PoW() < MinimumPoW && !wh.test { - log.Debug(fmt.Sprintf("envelope with low PoW dropped: %f [%x]", envelope.PoW(), envelope.Hash())) + if envelope.PoW() < wh.minPoW { + log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex()) return false, nil // drop envelope without error } @@ -474,10 +581,10 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) { wh.poolMu.Unlock() if alreadyCached { - log.Trace(fmt.Sprintf("whisper envelope already cached [%x]\n", envelope.Hash())) + log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex()) } else { - log.Trace(fmt.Sprintf("cached whisper envelope [%x]: %v\n", envelope.Hash(), envelope)) - wh.stats.totalMemoryUsed += envelope.size() + log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex()) + wh.stats.memoryUsed += envelope.size() wh.postEvent(envelope, false) // notify the local node about the new message if wh.mailServer != nil { wh.mailServer.Archive(envelope) @@ -508,11 +615,12 @@ func (w *Whisper) checkOverflow() { if queueSize == messageQueueLimit { if !w.overflow { w.overflow = true - log.Warn(fmt.Sprint("message queue overflow")) + log.Warn("message queue overflow") } } else if queueSize <= messageQueueLimit/2 { if w.overflow { w.overflow = false + log.Warn("message queue overflow fixed (back to normal)") } } } @@ -558,19 +666,17 @@ func (w *Whisper) expire() { w.poolMu.Lock() defer w.poolMu.Unlock() - w.stats.clear() + w.stats.reset() now := uint32(time.Now().Unix()) for expiry, hashSet := range w.expirations { if expiry < now { - w.stats.messagesCleared++ - // Dump all expired messages and remove timestamp hashSet.Each(func(v interface{}) bool { sz := w.envelopes[v.(common.Hash)].size() - w.stats.memoryCleared += sz - w.stats.totalMemoryUsed -= sz delete(w.envelopes, v.(common.Hash)) - delete(w.messages, v.(common.Hash)) + w.stats.messagesCleared++ + w.stats.memoryCleared += sz + w.stats.memoryUsed -= sz return true }) w.expirations[expiry].Clear() @@ -579,12 +685,21 @@ func (w *Whisper) expire() { } } +// Stats returns the whisper node statistics. func (w *Whisper) Stats() string { - return fmt.Sprintf("Latest expiry cycle cleared %d messages (%d bytes). Memory usage: %d bytes.", - w.stats.messagesCleared, w.stats.memoryCleared, w.stats.totalMemoryUsed) + result := fmt.Sprintf("Memory usage: %d bytes. Average messages cleared per expiry cycle: %d. Total messages cleared: %d.", + w.stats.memoryUsed, w.stats.totalMessagesCleared/w.stats.cycles, w.stats.totalMessagesCleared) + if w.stats.messagesCleared > 0 { + result += fmt.Sprintf(" Latest expiry cycle cleared %d messages (%d bytes).", + w.stats.messagesCleared, w.stats.memoryCleared) + } + if w.overflow { + result += " Message queue state: overflow." + } + return result } -// envelopes retrieves all the messages currently pooled by the node. +// Envelopes retrieves all the messages currently pooled by the node. func (w *Whisper) Envelopes() []*Envelope { w.poolMu.RLock() defer w.poolMu.RUnlock() @@ -596,15 +711,17 @@ func (w *Whisper) Envelopes() []*Envelope { return all } -// Messages retrieves all the decrypted messages matching a filter id. +// Messages iterates through all currently floating envelopes +// and retrieves all the messages, that this filter could decrypt. func (w *Whisper) Messages(id string) []*ReceivedMessage { result := make([]*ReceivedMessage, 0) w.poolMu.RLock() defer w.poolMu.RUnlock() if filter := w.filters.Get(id); filter != nil { - for _, msg := range w.messages { - if filter.MatchMessage(msg) { + for _, env := range w.envelopes { + msg := filter.processEnvelope(env) + if msg != nil { result = append(result, msg) } } @@ -612,6 +729,7 @@ func (w *Whisper) Messages(id string) []*ReceivedMessage { return result } +// isEnvelopeCached checks if envelope with specific hash has already been received and cached. func (w *Whisper) isEnvelopeCached(hash common.Hash) bool { w.poolMu.Lock() defer w.poolMu.Unlock() @@ -620,22 +738,30 @@ func (w *Whisper) isEnvelopeCached(hash common.Hash) bool { return exist } -func (w *Whisper) addDecryptedMessage(msg *ReceivedMessage) { - w.poolMu.Lock() - defer w.poolMu.Unlock() - - w.messages[msg.EnvelopeHash] = msg -} +// reset resets the node's statistics after each expiry cycle. +func (s *Statistics) reset() { + s.cycles++ + s.totalMessagesCleared += s.messagesCleared -func (s *Statistics) clear() { s.memoryCleared = 0 s.messagesCleared = 0 } +// ValidateKeyID checks the format of key id. +func ValidateKeyID(id string) error { + const target = keyIdSize * 2 + if len(id) != target { + return fmt.Errorf("wrong size of key ID (expected %d bytes, got %d)", target, len(id)) + } + return nil +} + +// ValidatePublicKey checks the format of the given public key. func ValidatePublicKey(k *ecdsa.PublicKey) bool { return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 } +// validatePrivateKey checks the format of the given private key. func validatePrivateKey(k *ecdsa.PrivateKey) bool { if k == nil || k.D == nil || k.D.Sign() == 0 { return false @@ -648,6 +774,7 @@ func validateSymmetricKey(k []byte) bool { return len(k) > 0 && !containsOnlyZeros(k) } +// containsOnlyZeros checks if the data contain only zeros. func containsOnlyZeros(data []byte) bool { for _, b := range data { if b != 0 { @@ -657,7 +784,8 @@ func containsOnlyZeros(data []byte) bool { return true } -func bytesToIntLittleEndian(b []byte) (res uint64) { +// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer. +func bytesToUintLittleEndian(b []byte) (res uint64) { mul := uint64(1) for i := 0; i < len(b); i++ { res += uint64(b[i]) * mul @@ -666,7 +794,8 @@ func bytesToIntLittleEndian(b []byte) (res uint64) { return res } -func BytesToIntBigEndian(b []byte) (res uint64) { +// BytesToUintBigEndian converts the slice to 64-bit unsigned integer. +func BytesToUintBigEndian(b []byte) (res uint64) { for i := 0; i < len(b); i++ { res *= 256 res += uint64(b[i]) @@ -674,7 +803,7 @@ func BytesToIntBigEndian(b []byte) (res uint64) { return res } -// DeriveSymmetricKey derives symmetric key material from the key or password. +// deriveKeyMaterial derives symmetric key material from the key or password. // pbkdf2 is used for security, in case people use password instead of randomly generated keys. func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) { if version == 0 { @@ -686,3 +815,17 @@ func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error return nil, unknownVersionError(version) } } + +// GenerateRandomID generates a random string, which is then returned to be used as a key id +func GenerateRandomID() (id string, err error) { + buf := make([]byte, keyIdSize) + _, err = crand.Read(buf) + if err != nil { + return "", err + } + if !validateSymmetricKey(buf) { + return "", fmt.Errorf("error in generateRandomID: crypto/rand failed to generate random data") + } + id = common.Bytes2Hex(buf) + return id, err +} diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go index 312dacfc4..d5668259e 100644 --- a/whisper/whisperv5/whisper_test.go +++ b/whisper/whisperv5/whisper_test.go @@ -18,11 +18,9 @@ package whisperv5 import ( "bytes" + mrand "math/rand" "testing" "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" ) func TestWhisperBasic(t *testing.T) { @@ -49,21 +47,24 @@ func TestWhisperBasic(t *testing.T) { } peerID := make([]byte, 64) - randomize(peerID) + mrand.Read(peerID) peer, _ := w.getPeer(peerID) if peer != nil { t.Fatal("found peer for random key.") } - if err := w.MarkPeerTrusted(peerID); err == nil { + if err := w.AllowP2PMessagesFromPeer(peerID); err == nil { t.Fatalf("failed MarkPeerTrusted.") } exist := w.HasSymKey("non-existing") if exist { t.Fatalf("failed HasSymKey.") } - key := w.GetSymKey("non-existing") + key, err := w.GetSymKey("non-existing") + if err == nil { + t.Fatalf("failed GetSymKey(non-existing): false positive.") + } if key != nil { - t.Fatalf("failed GetSymKey.") + t.Fatalf("failed GetSymKey: false positive.") } mail := w.Envelopes() if len(mail) != 0 { @@ -79,7 +80,7 @@ func TestWhisperBasic(t *testing.T) { if _, err := deriveKeyMaterial(peerID, ver); err != unknownVersionError(ver) { t.Fatalf("failed deriveKeyMaterial with param = %v: %s.", peerID, err) } - derived, err := deriveKeyMaterial(peerID, 0) + derived, err = deriveKeyMaterial(peerID, 0) if err != nil { t.Fatalf("failed second deriveKeyMaterial with param = %v: %s.", peerID, err) } @@ -91,8 +92,8 @@ func TestWhisperBasic(t *testing.T) { } buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0} - le := bytesToIntLittleEndian(buf) - be := BytesToIntBigEndian(buf) + le := bytesToUintLittleEndian(buf) + be := BytesToUintBigEndian(buf) if le != uint64(0x280e5ff) { t.Fatalf("failed bytesToIntLittleEndian: %d.", le) } @@ -100,7 +101,14 @@ func TestWhisperBasic(t *testing.T) { t.Fatalf("failed BytesToIntBigEndian: %d.", be) } - pk := w.NewIdentity() + id, err := w.NewKeyPair() + if err != nil { + t.Fatalf("failed to generate new key pair: %s.", err) + } + pk, err := w.GetPrivateKey(id) + if err != nil { + t.Fatalf("failed to retrieve new key pair: %s.", err) + } if !validatePrivateKey(pk) { t.Fatalf("failed validatePrivateKey: %v.", pk) } @@ -111,67 +119,112 @@ func TestWhisperBasic(t *testing.T) { func TestWhisperIdentityManagement(t *testing.T) { w := New() - id1 := w.NewIdentity() - id2 := w.NewIdentity() - pub1 := common.ToHex(crypto.FromECDSAPub(&id1.PublicKey)) - pub2 := common.ToHex(crypto.FromECDSAPub(&id2.PublicKey)) - pk1 := w.GetIdentity(pub1) - pk2 := w.GetIdentity(pub2) - if !w.HasIdentity(pub1) { - t.Fatalf("failed HasIdentity(pub1).") + id1, err := w.NewKeyPair() + if err != nil { + t.Fatalf("failed to generate new key pair: %s.", err) } - if !w.HasIdentity(pub2) { - t.Fatalf("failed HasIdentity(pub2).") + id2, err := w.NewKeyPair() + if err != nil { + t.Fatalf("failed to generate new key pair: %s.", err) } - if pk1 != id1 { - t.Fatalf("failed GetIdentity(pub1).") + pk1, err := w.GetPrivateKey(id1) + if err != nil { + t.Fatalf("failed to retrieve the key pair: %s.", err) + } + pk2, err := w.GetPrivateKey(id2) + if err != nil { + t.Fatalf("failed to retrieve the key pair: %s.", err) + } + + if !w.HasKeyPair(id1) { + t.Fatalf("failed HasIdentity(pk1).") + } + if !w.HasKeyPair(id2) { + t.Fatalf("failed HasIdentity(pk2).") + } + if pk1 == nil { + t.Fatalf("failed GetIdentity(pk1).") + } + if pk2 == nil { + t.Fatalf("failed GetIdentity(pk2).") + } + + if !validatePrivateKey(pk1) { + t.Fatalf("pk1 is invalid.") } - if pk2 != id2 { - t.Fatalf("failed GetIdentity(pub2).") + if !validatePrivateKey(pk2) { + t.Fatalf("pk2 is invalid.") } // Delete one identity - w.DeleteIdentity(pub1) - pk1 = w.GetIdentity(pub1) - pk2 = w.GetIdentity(pub2) - if w.HasIdentity(pub1) { + done := w.DeleteKeyPair(id1) + if !done { + t.Fatalf("failed to delete id1.") + } + pk1, err = w.GetPrivateKey(id1) + if err == nil { + t.Fatalf("retrieve the key pair: false positive.") + } + pk2, err = w.GetPrivateKey(id2) + if err != nil { + t.Fatalf("failed to retrieve the key pair: %s.", err) + } + if w.HasKeyPair(id1) { t.Fatalf("failed DeleteIdentity(pub1): still exist.") } - if !w.HasIdentity(pub2) { + if !w.HasKeyPair(id2) { t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.") } if pk1 != nil { t.Fatalf("failed DeleteIdentity(pub1): first key still exist.") } - if pk2 != id2 { + if pk2 == nil { t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.") } // Delete again non-existing identity - w.DeleteIdentity(pub1) - pk1 = w.GetIdentity(pub1) - pk2 = w.GetIdentity(pub2) - if w.HasIdentity(pub1) { + done = w.DeleteKeyPair(id1) + if done { + t.Fatalf("delete id1: false positive.") + } + pk1, err = w.GetPrivateKey(id1) + if err == nil { + t.Fatalf("retrieve the key pair: false positive.") + } + pk2, err = w.GetPrivateKey(id2) + if err != nil { + t.Fatalf("failed to retrieve the key pair: %s.", err) + } + if w.HasKeyPair(id1) { t.Fatalf("failed delete non-existing identity: exist.") } - if !w.HasIdentity(pub2) { + if !w.HasKeyPair(id2) { t.Fatalf("failed delete non-existing identity: pub2 does not exist.") } if pk1 != nil { t.Fatalf("failed delete non-existing identity: first key exist.") } - if pk2 != id2 { + if pk2 == nil { t.Fatalf("failed delete non-existing identity: second key does not exist.") } // Delete second identity - w.DeleteIdentity(pub2) - pk1 = w.GetIdentity(pub1) - pk2 = w.GetIdentity(pub2) - if w.HasIdentity(pub1) { + done = w.DeleteKeyPair(id2) + if !done { + t.Fatalf("failed to delete id2.") + } + pk1, err = w.GetPrivateKey(id1) + if err == nil { + t.Fatalf("retrieve the key pair: false positive.") + } + pk2, err = w.GetPrivateKey(id2) + if err == nil { + t.Fatalf("retrieve the key pair: false positive.") + } + if w.HasKeyPair(id1) { t.Fatalf("failed delete second identity: first identity exist.") } - if w.HasIdentity(pub2) { + if w.HasKeyPair(id2) { t.Fatalf("failed delete second identity: still exist.") } if pk1 != nil { @@ -185,23 +238,30 @@ func TestWhisperIdentityManagement(t *testing.T) { func TestWhisperSymKeyManagement(t *testing.T) { InitSingleTest() + var err error var k1, k2 []byte w := New() id1 := string("arbitrary-string-1") id2 := string("arbitrary-string-2") - err := w.GenerateSymKey(id1) + id1, err = w.GenerateSymKey() if err != nil { t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err) } - k1 = w.GetSymKey(id1) - k2 = w.GetSymKey(id2) + k1, err = w.GetSymKey(id1) + if err != nil { + t.Fatalf("failed GetSymKey(id1).") + } + k2, err = w.GetSymKey(id2) + if err == nil { + t.Fatalf("failed GetSymKey(id2): false positive.") + } if !w.HasSymKey(id1) { t.Fatalf("failed HasSymKey(id1).") } if w.HasSymKey(id2) { - t.Fatalf("failed HasSymKey(id2).") + t.Fatalf("failed HasSymKey(id2): false positive.") } if k1 == nil { t.Fatalf("first key does not exist.") @@ -211,37 +271,49 @@ func TestWhisperSymKeyManagement(t *testing.T) { } // add existing id, nothing should change - randomKey := make([]byte, 16) - randomize(randomKey) - err = w.AddSymKey(id1, randomKey) - if err == nil { - t.Fatalf("failed AddSymKey with seed %d.", seed) + randomKey := make([]byte, aesKeyLength) + mrand.Read(randomKey) + id1, err = w.AddSymKeyDirect(randomKey) + if err != nil { + t.Fatalf("failed AddSymKey with seed %d: %s.", seed, err) } - k1 = w.GetSymKey(id1) - k2 = w.GetSymKey(id2) + k1, err = w.GetSymKey(id1) + if err != nil { + t.Fatalf("failed w.GetSymKey(id1).") + } + k2, err = w.GetSymKey(id2) + if err == nil { + t.Fatalf("failed w.GetSymKey(id2): false positive.") + } if !w.HasSymKey(id1) { t.Fatalf("failed w.HasSymKey(id1).") } if w.HasSymKey(id2) { - t.Fatalf("failed w.HasSymKey(id2).") + t.Fatalf("failed w.HasSymKey(id2): false positive.") } if k1 == nil { t.Fatalf("first key does not exist.") } - if bytes.Equal(k1, randomKey) { - t.Fatalf("k1 == randomKey.") + if !bytes.Equal(k1, randomKey) { + t.Fatalf("k1 != randomKey.") } if k2 != nil { t.Fatalf("second key already exist.") } - err = w.AddSymKey(id2, randomKey) // add non-existing (yet) + id2, err = w.AddSymKeyDirect(randomKey) if err != nil { t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err) } - k1 = w.GetSymKey(id1) - k2 = w.GetSymKey(id2) + k1, err = w.GetSymKey(id1) + if err != nil { + t.Fatalf("failed w.GetSymKey(id1).") + } + k2, err = w.GetSymKey(id2) + if err != nil { + t.Fatalf("failed w.GetSymKey(id2).") + } if !w.HasSymKey(id1) { t.Fatalf("HasSymKey(id1) failed.") } @@ -254,11 +326,11 @@ func TestWhisperSymKeyManagement(t *testing.T) { if k2 == nil { t.Fatalf("k2 does not exist.") } - if bytes.Equal(k1, k2) { - t.Fatalf("k1 == k2.") + if !bytes.Equal(k1, k2) { + t.Fatalf("k1 != k2.") } - if bytes.Equal(k1, randomKey) { - t.Fatalf("k1 == randomKey.") + if !bytes.Equal(k1, randomKey) { + t.Fatalf("k1 != randomKey.") } if len(k1) != aesKeyLength { t.Fatalf("wrong length of k1.") @@ -268,8 +340,17 @@ func TestWhisperSymKeyManagement(t *testing.T) { } w.DeleteSymKey(id1) - k1 = w.GetSymKey(id1) - k2 = w.GetSymKey(id2) + k1, err = w.GetSymKey(id1) + if err == nil { + t.Fatalf("failed w.GetSymKey(id1): false positive.") + } + if k1 != nil { + t.Fatalf("failed GetSymKey(id1): false positive.") + } + k2, err = w.GetSymKey(id2) + if err != nil { + t.Fatalf("failed w.GetSymKey(id2).") + } if w.HasSymKey(id1) { t.Fatalf("failed to delete first key: still exist.") } @@ -285,8 +366,17 @@ func TestWhisperSymKeyManagement(t *testing.T) { w.DeleteSymKey(id1) w.DeleteSymKey(id2) - k1 = w.GetSymKey(id1) - k2 = w.GetSymKey(id2) + k1, err = w.GetSymKey(id1) + if err == nil { + t.Fatalf("failed w.GetSymKey(id1): false positive.") + } + k2, err = w.GetSymKey(id2) + if err == nil { + t.Fatalf("failed w.GetSymKey(id2): false positive.") + } + if k1 != nil || k2 != nil { + t.Fatalf("k1 or k2 is not nil") + } if w.HasSymKey(id1) { t.Fatalf("failed to delete second key: first key exist.") } @@ -299,13 +389,63 @@ func TestWhisperSymKeyManagement(t *testing.T) { if k2 != nil { t.Fatalf("failed to delete second key: second key is not nil.") } + + randomKey = make([]byte, aesKeyLength+1) + mrand.Read(randomKey) + id1, err = w.AddSymKeyDirect(randomKey) + if err == nil { + t.Fatalf("added the key with wrong size, seed %d.", seed) + } + + const password = "arbitrary data here" + id1, err = w.AddSymKeyFromPassword(password) + if err != nil { + t.Fatalf("failed AddSymKeyFromPassword(id1) with seed %d: %s.", seed, err) + } + id2, err = w.AddSymKeyFromPassword(password) + if err != nil { + t.Fatalf("failed AddSymKeyFromPassword(id2) with seed %d: %s.", seed, err) + } + k1, err = w.GetSymKey(id1) + if err != nil { + t.Fatalf("failed w.GetSymKey(id1).") + } + k2, err = w.GetSymKey(id2) + if err != nil { + t.Fatalf("failed w.GetSymKey(id2).") + } + if !w.HasSymKey(id1) { + t.Fatalf("HasSymKey(id1) failed.") + } + if !w.HasSymKey(id2) { + t.Fatalf("HasSymKey(id2) failed.") + } + if k1 == nil { + t.Fatalf("k1 does not exist.") + } + if k2 == nil { + t.Fatalf("k2 does not exist.") + } + if !bytes.Equal(k1, k2) { + t.Fatalf("k1 != k2.") + } + if len(k1) != aesKeyLength { + t.Fatalf("wrong length of k1.") + } + if len(k2) != aesKeyLength { + t.Fatalf("wrong length of k2.") + } + if !validateSymmetricKey(k2) { + t.Fatalf("key validation failed.") + } } func TestExpiry(t *testing.T) { InitSingleTest() w := New() - w.test = true + w.SetMinimumPoW(0.0000001) + defer w.SetMinimumPoW(DefaultMinimumPoW) w.Start(nil) defer w.Stop() @@ -353,3 +493,87 @@ func TestExpiry(t *testing.T) { t.Fatalf("expire failed, seed: %d.", seed) } } + +func TestCustomization(t *testing.T) { + InitSingleTest() + + w := New() + defer w.SetMinimumPoW(DefaultMinimumPoW) + defer w.SetMaxMessageLength(DefaultMaxMessageLength) + w.Start(nil) + defer w.Stop() + + const smallPoW = 0.00001 + + f, err := generateFilter(t, true) + params, err := generateMessageParams() + if err != nil { + t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) + } + + params.KeySym = f.KeySym + params.Topic = BytesToTopic(f.Topics[2]) + params.PoW = smallPoW + params.TTL = 3600 * 24 // one day + msg := NewSentMessage(params) + env, err := msg.Wrap(params) + if err != nil { + t.Fatalf("failed Wrap with seed %d: %s.", seed, err) + } + + err = w.Send(env) + if err == nil { + t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed) + } + + w.SetMinimumPoW(smallPoW / 2) + err = w.Send(env) + if err != nil { + t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) + } + + params.TTL++ + msg = NewSentMessage(params) + env, err = msg.Wrap(params) + if err != nil { + t.Fatalf("failed Wrap with seed %d: %s.", seed, err) + } + w.SetMaxMessageLength(env.size() - 1) + err = w.Send(env) + if err == nil { + t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed) + } + + w.SetMaxMessageLength(DefaultMaxMessageLength) + err = w.Send(env) + if err != nil { + t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err) + } + + // wait till received or timeout + var received bool + for j := 0; j < 20; j++ { + time.Sleep(100 * time.Millisecond) + if len(w.Envelopes()) > 1 { + received = true + break + } + } + + if !received { + t.Fatalf("did not receive the sent envelope, seed: %d.", seed) + } + + // check w.messages() + id, err := w.Subscribe(f) + time.Sleep(5 * time.Millisecond) + mail := f.Retrieve() + if len(mail) > 0 { + t.Fatalf("received premature mail") + } + + mail = w.Messages(id) + if len(mail) != 2 { + t.Fatalf("failed to get whisper messages") + } +} |